mt5cli 1.4.0__tar.gz → 1.4.2__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.2/.agents/skills/local-qa/scripts/qa.sh +42 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/.agents/skills/mt5cli/SKILL.md +6 -4
- mt5cli-1.4.2/.github/CODEOWNERS +2 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/.github/workflows/ci.yml +11 -14
- {mt5cli-1.4.0 → mt5cli-1.4.2}/.github/workflows/claude.yml +2 -2
- {mt5cli-1.4.0 → mt5cli-1.4.2}/.github/workflows/release.yml +2 -2
- mt5cli-1.4.2/AGENTS.md +29 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/PKG-INFO +21 -15
- {mt5cli-1.4.0 → mt5cli-1.4.2}/README.md +19 -13
- {mt5cli-1.4.0 → mt5cli-1.4.2}/docs/api/history.md +43 -109
- {mt5cli-1.4.0 → mt5cli-1.4.2}/docs/api/public-contract.md +61 -57
- {mt5cli-1.4.0 → mt5cli-1.4.2}/docs/api/trading.md +23 -17
- {mt5cli-1.4.0 → mt5cli-1.4.2}/docs/index.md +16 -10
- mt5cli-1.4.2/mt5cli/__init__.py +18 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/mt5cli/cli.py +39 -52
- {mt5cli-1.4.0 → mt5cli-1.4.2}/mt5cli/client.py +24 -23
- {mt5cli-1.4.0 → mt5cli-1.4.2}/mt5cli/contract.py +6 -76
- {mt5cli-1.4.0 → mt5cli-1.4.2}/mt5cli/converters.py +70 -14
- {mt5cli-1.4.0 → mt5cli-1.4.2}/mt5cli/history.py +209 -687
- {mt5cli-1.4.0 → mt5cli-1.4.2}/mt5cli/marketdata.py +1 -1
- mt5cli-1.4.2/mt5cli/rates.py +538 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/mt5cli/schemas.py +102 -44
- mt5cli-1.4.0/mt5cli/__init__.py → mt5cli-1.4.2/mt5cli/sdk.py +19 -22
- {mt5cli-1.4.0 → mt5cli-1.4.2}/mt5cli/trading.py +6 -44
- {mt5cli-1.4.0 → mt5cli-1.4.2}/pyproject.toml +5 -5
- mt5cli-1.4.2/tests/cli/test_cli.bats +127 -0
- {mt5cli-1.4.0/tests → mt5cli-1.4.2/tests/unit}/test_cli.py +37 -110
- {mt5cli-1.4.0/tests → mt5cli-1.4.2/tests/unit}/test_client.py +105 -29
- {mt5cli-1.4.0/tests → mt5cli-1.4.2/tests/unit}/test_contracts.py +120 -29
- mt5cli-1.4.2/tests/unit/test_docs_contract.py +40 -0
- {mt5cli-1.4.0/tests → mt5cli-1.4.2/tests/unit}/test_examples.py +1 -1
- {mt5cli-1.4.0/tests → mt5cli-1.4.2/tests/unit}/test_history.py +588 -2013
- mt5cli-1.4.2/tests/unit/test_history_contract.py +371 -0
- mt5cli-1.4.2/tests/unit/test_history_edge_contract.py +122 -0
- mt5cli-1.4.2/tests/unit/test_rate_timestamp_contract.py +291 -0
- mt5cli-1.4.2/tests/unit/test_rates.py +380 -0
- mt5cli-1.4.2/tests/unit/test_rates_contract.py +220 -0
- mt5cli-1.4.2/tests/unit/test_stable_sdk_cleanup.py +52 -0
- {mt5cli-1.4.0/tests → mt5cli-1.4.2/tests/unit}/test_trading.py +0 -86
- {mt5cli-1.4.0 → mt5cli-1.4.2}/uv.lock +219 -13
- mt5cli-1.4.0/.agents/skills/local-qa/scripts/qa.sh +0 -26
- mt5cli-1.4.0/.agents/skills/pr-feedback-triage/SKILL.md +0 -211
- mt5cli-1.4.0/AGENTS.md +0 -37
- {mt5cli-1.4.0 → mt5cli-1.4.2}/.agents/skills/local-qa/SKILL.md +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/.claude/agents/codex.md +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/.claude/settings.json +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/.github/FUNDING.yml +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/.github/dependabot.yml +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/.github/renovate.json +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/.gitignore +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/CLAUDE.md +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/LICENSE +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/docs/api/cli.md +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/docs/api/client.md +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/docs/api/converters.md +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/docs/api/exceptions.md +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/docs/api/grafana.md +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/docs/api/index.md +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/docs/api/marketdata.md +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/docs/api/observability.md +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/docs/api/schemas.md +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/docs/api/telemetry.md +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/docs/api/utils.md +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/examples/grafana/README.md +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/examples/grafana/compose.yml +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/examples/grafana/dashboards/mt5cli-market.json +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/examples/grafana/dashboards/mt5cli-overview.json +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/examples/grafana/dashboards/mt5cli-trades.json +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/examples/grafana/provisioning/dashboards/mt5cli.yml +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/examples/grafana/provisioning/datasources/mt5cli-sqlite.yml +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/mkdocs.yml +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/mt5cli/__main__.py +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/mt5cli/exceptions.py +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/mt5cli/grafana.py +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/mt5cli/observability.py +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/mt5cli/retry.py +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/mt5cli/telemetry.py +0 -0
- {mt5cli-1.4.0 → mt5cli-1.4.2}/mt5cli/utils.py +0 -0
- {mt5cli-1.4.0/tests → mt5cli-1.4.2/tests/unit}/__init__.py +0 -0
- {mt5cli-1.4.0/tests → mt5cli-1.4.2/tests/unit}/conftest.py +0 -0
- {mt5cli-1.4.0/tests → mt5cli-1.4.2/tests/unit}/test_grafana.py +0 -0
- {mt5cli-1.4.0/tests → mt5cli-1.4.2/tests/unit}/test_marketdata.py +0 -0
- {mt5cli-1.4.0/tests → mt5cli-1.4.2/tests/unit}/test_observability.py +0 -0
- {mt5cli-1.4.0/tests → mt5cli-1.4.2/tests/unit}/test_packaging.py +0 -0
- {mt5cli-1.4.0/tests → mt5cli-1.4.2/tests/unit}/test_telemetry.py +0 -0
- {mt5cli-1.4.0/tests → mt5cli-1.4.2/tests/unit}/test_utils.py +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
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 sync
|
|
13
|
+
uv run ruff format .
|
|
14
|
+
uv run ruff check --fix .
|
|
15
|
+
uv run pyright .
|
|
16
|
+
uv run pytest
|
|
17
|
+
|
|
18
|
+
# Markdown and JSON
|
|
19
|
+
npx -y prettier --write './**/*.{md,json}'
|
|
20
|
+
|
|
21
|
+
# YAML
|
|
22
|
+
git ls-files -z -- '*.yml' \
|
|
23
|
+
| xargs -0 -t uvx yamllint -d '{"extends": "relaxed", "rules": {"line-length": "disable"}}'
|
|
24
|
+
|
|
25
|
+
# GitHub Actions
|
|
26
|
+
case "${OSTYPE}" in
|
|
27
|
+
darwin* | linux*)
|
|
28
|
+
# Shell scripts
|
|
29
|
+
git ls-files -z -- '*.sh' '*.bash' '*.bats' \
|
|
30
|
+
| xargs -0 -t shfmt --write --indent=2 --binary-next-line --case-indent --space-redirects
|
|
31
|
+
git ls-files -z -- '*.sh' '*.bash' '*.bats' \
|
|
32
|
+
| xargs -0 -t shellcheck
|
|
33
|
+
|
|
34
|
+
# GitHub Actions
|
|
35
|
+
zizmor --fix=safe .github/workflows
|
|
36
|
+
git ls-files -z -- '.github/workflows/*.yml' | xargs -0 -t actionlint
|
|
37
|
+
checkov --framework=all --output=github_failed_only --directory=.
|
|
38
|
+
;;
|
|
39
|
+
*)
|
|
40
|
+
echo 'GitHub Actions and shell script linting is only supported on Linux and macOS.'
|
|
41
|
+
;;
|
|
42
|
+
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
|
|
|
@@ -41,8 +41,10 @@ Global options MUST precede the subcommand.
|
|
|
41
41
|
|
|
42
42
|
### Parameter value formats
|
|
43
43
|
|
|
44
|
-
- **Datetimes** (`--date-from`, `--date-to`): ISO 8601
|
|
45
|
-
`2024-01-01T12:00:00
|
|
44
|
+
- **Datetimes** (`--date-from`, `--date-to`): offset-free ISO 8601 values
|
|
45
|
+
(`2024-01-01` or `2024-01-01T12:00:00`). MT5 bounds are timezone-naive
|
|
46
|
+
trade-server wall-clock values; offset-bearing values are rejected and no
|
|
47
|
+
UTC-to-server-time conversion is inferred.
|
|
46
48
|
- **Timeframe** (`--timeframe`): `M1`, `M2`, `M3`, `M4`, `M5`, `M6`, `M10`,
|
|
47
49
|
`M12`, `M15`, `M20`, `M30`, `H1`, `H2`, `H3`, `H4`, `H6`, `H8`, `H12`,
|
|
48
50
|
`D1`, `W1`, `MN1`, or the raw integer.
|
|
@@ -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.2/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.4.
|
|
3
|
+
Version: 1.4.2
|
|
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
|
|
@@ -50,7 +50,7 @@ Built on top of [pdmt5](https://github.com/dceoy/pdmt5), a pandas-based data han
|
|
|
50
50
|
- **Comprehensive data access**: Rates, ticks, account info, symbols, orders, positions, and trading history
|
|
51
51
|
- **Flexible timeframes**: Named timeframes (M1, H1, D1, etc.) and numeric values
|
|
52
52
|
- **Connection management**: Optional credentials, server, and timeout configuration
|
|
53
|
-
- **SQLite rate loading**: Load
|
|
53
|
+
- **SQLite rate loading**: Load canonical normalized `rates` storage for offline workflows
|
|
54
54
|
|
|
55
55
|
## Installation
|
|
56
56
|
|
|
@@ -69,7 +69,7 @@ pip install -U "mt5cli[parquet]" MetaTrader5
|
|
|
69
69
|
Import `MT5Client` for generic MT5 data access, schema normalization, and optional order primitives.
|
|
70
70
|
|
|
71
71
|
```python
|
|
72
|
-
from datetime import
|
|
72
|
+
from datetime import datetime
|
|
73
73
|
from pathlib import Path
|
|
74
74
|
|
|
75
75
|
from mt5cli import (
|
|
@@ -103,8 +103,8 @@ export_dataframe(closed_rates, Path("rates.csv"), "csv")
|
|
|
103
103
|
collect_history(
|
|
104
104
|
Path("history.db"),
|
|
105
105
|
symbols=["EURUSD"],
|
|
106
|
-
date_from=datetime(2024, 1, 1
|
|
107
|
-
date_to=datetime(2024, 2, 1
|
|
106
|
+
date_from=datetime(2024, 1, 1),
|
|
107
|
+
date_to=datetime(2024, 2, 1),
|
|
108
108
|
datasets={Dataset.rates, Dataset.history_deals},
|
|
109
109
|
)
|
|
110
110
|
|
|
@@ -113,9 +113,16 @@ update_history_with_config(
|
|
|
113
113
|
output="history.db",
|
|
114
114
|
symbols=["EURUSD"],
|
|
115
115
|
config=build_config(login=12345),
|
|
116
|
+
date_to=datetime(2024, 2, 1),
|
|
116
117
|
)
|
|
117
118
|
```
|
|
118
119
|
|
|
120
|
+
MT5 query bounds are timezone-naive trade-server wall-clock datetimes, as
|
|
121
|
+
required by pdmt5. Incremental history updates require an explicit `date_to`
|
|
122
|
+
because mt5cli cannot determine the current trade-server time. Offset-bearing
|
|
123
|
+
strings and timezone-aware `datetime` objects are rejected; mt5cli does not
|
|
124
|
+
infer or strip offsets to perform a conversion.
|
|
125
|
+
|
|
119
126
|
Schema contracts live in `mt5cli.schemas` (`DataKind`, `validate_schema`, `normalize_dataframe`). Export and storage helpers are in `mt5cli.utils` (`Dataset`, `export_dataframe`) and `mt5cli.history`.
|
|
120
127
|
|
|
121
128
|
`MT5Client.order_send()` is a live execution primitive: it can place real trades on the connected account. mt5cli does not implement strategy logic, signal generation, backtesting, or optimization — downstream applications must gate live execution explicitly.
|
|
@@ -335,6 +342,8 @@ OpenTelemetry metrics are documented in [`docs/api/telemetry.md`](docs/api/telem
|
|
|
335
342
|
For automated pipelines, use the importable incremental API instead of re-fetching fixed date ranges:
|
|
336
343
|
|
|
337
344
|
```python
|
|
345
|
+
from datetime import datetime
|
|
346
|
+
|
|
338
347
|
from pdmt5 import Mt5Config
|
|
339
348
|
from mt5cli import update_history_with_config
|
|
340
349
|
from mt5cli.utils import Dataset
|
|
@@ -347,19 +356,17 @@ update_history_with_config(
|
|
|
347
356
|
datasets={Dataset.rates, Dataset.history_deals},
|
|
348
357
|
timeframes=["M1", "H1"], # default: all fixed MT5 timeframes
|
|
349
358
|
lookback_hours=24,
|
|
350
|
-
|
|
359
|
+
date_to=datetime(2024, 2, 1),
|
|
351
360
|
with_views=True,
|
|
352
361
|
include_account_events=True,
|
|
353
362
|
)
|
|
354
363
|
```
|
|
355
364
|
|
|
356
365
|
- **`collect-history`**: explicit date-range export into SQLite.
|
|
357
|
-
- **`update_history`**: incremental append based on existing SQLite `MAX(time)` per symbol (and timeframe for rates); account-level deals use a separate cursor when `include_account_events=True`.
|
|
366
|
+
- **`update_history`**: incremental append based on existing SQLite `MAX(time)` per symbol (and timeframe for rates); account-level deals use a separate cursor when `include_account_events=True`. Pass an explicit naive trade-server `date_to`; mt5cli cannot determine the current server time. Rates are persisted only in the canonical normalized `rates` table.
|
|
358
367
|
- **`rates` table**: normalized storage with `symbol` and `timeframe` columns.
|
|
359
|
-
- **
|
|
360
|
-
- **
|
|
361
|
-
- **Rate view loading**: use `load_rate_data()` / `load_rate_data_from_connection()` to load a SQLite rate table or view into a `DatetimeIndex` DataFrame.
|
|
362
|
-
- **Multi-series rate loading**: use `build_rate_targets()` to build neutral `RateTarget(symbol, timeframe)` pairs, `resolve_rate_tables()` to map them to table/view names (pass `require_existing=True` for strict resolution), and `load_rate_series_from_sqlite()` to load them into a mapping keyed by `(symbol, integer timeframe)`. The loader requires existing managed views unless `explicit_tables` is supplied, and rejects duplicate `(symbol, timeframe)` targets.
|
|
368
|
+
- **Explicit table loading**: use `load_rate_data()` / `load_rate_data_from_connection()` for intentionally named custom SQLite rate tables.
|
|
369
|
+
- **Multi-series rate loading**: use `build_rate_targets()` to build neutral `RateTarget(symbol, timeframe)` pairs and `load_rate_series_from_sqlite()` to load canonical `rates` rows into a mapping keyed by `(symbol, integer timeframe)`. Pass `explicit_tables` only for intentionally named custom tables; duplicate `(symbol, timeframe)` targets are rejected.
|
|
363
370
|
- **Multi-account latest rates**: use `collect_latest_rates_for_accounts()` with `AccountSpec` to read the latest bars for several account groups, merged into a `(symbol, integer timeframe)` mapping. For long-running pollers, `collect_latest_rates_for_accounts_with_retries()` adds bounded exponential backoff that retries only recoverable MT5 errors and re-raises once `retry_count` is exhausted.
|
|
364
371
|
- **Latest closed bars**: use `collect_latest_closed_rates_for_accounts()` when downstream logic must exclude the still-forming current bar. It fetches `count + 1` bars at `start_pos=0`, drops the last row with `drop_forming_rate_bar()`, and validates each series is non-empty. `collect_latest_closed_rates_by_granularity()` returns the same data keyed by `(symbol, granularity_name)` such as `("EURUSD", "M1")`.
|
|
365
372
|
|
|
@@ -376,7 +383,7 @@ eurusd_m1 = rates["EURUSD", "M1"] # closed bars only
|
|
|
376
383
|
```
|
|
377
384
|
|
|
378
385
|
- **Credential resolution**: use `resolve_account_spec()` / `resolve_account_specs()` to merge explicit override values over `AccountSpec` fields and expand `${ENV_VAR}` placeholders (via `substitute_env_placeholders()`), raising `ValueError` for missing variables. This keeps secrets out of plan/config files without coupling to any strategy code. For config dicts or nested structures loaded from YAML/TOML, use `substitute_mapping_values(data, keys={"login", "password"})` to expand placeholders only for caller-specified keys — key names are never hard-coded in mt5cli.
|
|
379
|
-
- **Throttled history updates**: use `ThrottledHistoryUpdater` to wrap `update_history()` with a minimum `interval_seconds` between successful runs (monotonic clock). Call `should_update()` / `update(client, symbols)` from an application loop; errors propagate by default, or pass `suppress_errors=True` to swallow recoverable `Mt5*Error`, `sqlite3.Error`, `ValueError`, and `OSError` without advancing the throttle (`AttributeError` / `TypeError` always propagate, since the client passed to `update()` must implement the canonical `HistoryClient` method names). Pass `update_backend` to inject a custom history update callable (same keyword arguments as `update_history`) instead of monkey-patching `mt5cli.history.update_history`.
|
|
386
|
+
- **Throttled history updates**: use `ThrottledHistoryUpdater` to wrap `update_history()` with a minimum `interval_seconds` between successful runs (monotonic clock). Call `should_update()` / `update(client, symbols, date_to=...)` from an application loop; errors propagate by default, or pass `suppress_errors=True` to swallow recoverable `Mt5*Error`, `sqlite3.Error`, `ValueError`, and `OSError` without advancing the throttle (`AttributeError` / `TypeError` always propagate, since the client passed to `update()` must implement the canonical `HistoryClient` method names). Pass `update_backend` to inject a custom history update callable (same keyword arguments as `update_history`) instead of monkey-patching `mt5cli.history.update_history`.
|
|
380
387
|
- **Trading helpers**: use `mt5_session()` for both market data and generic execution helpers. It owns initialization and shutdown for sessions it creates; caller-supplied clients remain caller-owned.
|
|
381
388
|
- **Granularity-keyed rate loading**: `load_rate_series_by_granularity()` builds targets with `build_rate_targets()`, loads them with `load_rate_series_from_sqlite()`, and returns a mapping keyed by `(symbol | None, granularity_name)` such as `("EURUSD", "M1")` to reduce downstream boilerplate.
|
|
382
389
|
- **MT5 session helper**: use the `mt5_session()` context manager to attach to (or, when `Mt5Config.path` is set, launch) an MT5 terminal, log in, and yield a connected `MT5Client` that shuts down on exit.
|
|
@@ -385,7 +392,6 @@ eurusd_m1 = rates["EURUSD", "M1"] # closed bars only
|
|
|
385
392
|
|
|
386
393
|
## Requirements
|
|
387
394
|
|
|
388
|
-
- Python 3.11+
|
|
389
395
|
- Windows OS (MetaTrader 5 requirement)
|
|
390
396
|
- MetaTrader 5 platform installed
|
|
391
397
|
|
|
@@ -435,7 +441,7 @@ updater = ThrottledHistoryUpdater(
|
|
|
435
441
|
output="history.db", interval_seconds=60, suppress_errors=True
|
|
436
442
|
)
|
|
437
443
|
with mt5_session(Mt5Config(login=login)) as client:
|
|
438
|
-
updater.update(client, ["EURUSD"])
|
|
444
|
+
updater.update(client, ["EURUSD"], date_to="2024-02-01")
|
|
439
445
|
```
|
|
440
446
|
|
|
441
447
|
Read-only collectors can keep using `mt5_session()` and `MT5Client`.
|
|
@@ -21,7 +21,7 @@ Built on top of [pdmt5](https://github.com/dceoy/pdmt5), a pandas-based data han
|
|
|
21
21
|
- **Comprehensive data access**: Rates, ticks, account info, symbols, orders, positions, and trading history
|
|
22
22
|
- **Flexible timeframes**: Named timeframes (M1, H1, D1, etc.) and numeric values
|
|
23
23
|
- **Connection management**: Optional credentials, server, and timeout configuration
|
|
24
|
-
- **SQLite rate loading**: Load
|
|
24
|
+
- **SQLite rate loading**: Load canonical normalized `rates` storage for offline workflows
|
|
25
25
|
|
|
26
26
|
## Installation
|
|
27
27
|
|
|
@@ -40,7 +40,7 @@ pip install -U "mt5cli[parquet]" MetaTrader5
|
|
|
40
40
|
Import `MT5Client` for generic MT5 data access, schema normalization, and optional order primitives.
|
|
41
41
|
|
|
42
42
|
```python
|
|
43
|
-
from datetime import
|
|
43
|
+
from datetime import datetime
|
|
44
44
|
from pathlib import Path
|
|
45
45
|
|
|
46
46
|
from mt5cli import (
|
|
@@ -74,8 +74,8 @@ export_dataframe(closed_rates, Path("rates.csv"), "csv")
|
|
|
74
74
|
collect_history(
|
|
75
75
|
Path("history.db"),
|
|
76
76
|
symbols=["EURUSD"],
|
|
77
|
-
date_from=datetime(2024, 1, 1
|
|
78
|
-
date_to=datetime(2024, 2, 1
|
|
77
|
+
date_from=datetime(2024, 1, 1),
|
|
78
|
+
date_to=datetime(2024, 2, 1),
|
|
79
79
|
datasets={Dataset.rates, Dataset.history_deals},
|
|
80
80
|
)
|
|
81
81
|
|
|
@@ -84,9 +84,16 @@ update_history_with_config(
|
|
|
84
84
|
output="history.db",
|
|
85
85
|
symbols=["EURUSD"],
|
|
86
86
|
config=build_config(login=12345),
|
|
87
|
+
date_to=datetime(2024, 2, 1),
|
|
87
88
|
)
|
|
88
89
|
```
|
|
89
90
|
|
|
91
|
+
MT5 query bounds are timezone-naive trade-server wall-clock datetimes, as
|
|
92
|
+
required by pdmt5. Incremental history updates require an explicit `date_to`
|
|
93
|
+
because mt5cli cannot determine the current trade-server time. Offset-bearing
|
|
94
|
+
strings and timezone-aware `datetime` objects are rejected; mt5cli does not
|
|
95
|
+
infer or strip offsets to perform a conversion.
|
|
96
|
+
|
|
90
97
|
Schema contracts live in `mt5cli.schemas` (`DataKind`, `validate_schema`, `normalize_dataframe`). Export and storage helpers are in `mt5cli.utils` (`Dataset`, `export_dataframe`) and `mt5cli.history`.
|
|
91
98
|
|
|
92
99
|
`MT5Client.order_send()` is a live execution primitive: it can place real trades on the connected account. mt5cli does not implement strategy logic, signal generation, backtesting, or optimization — downstream applications must gate live execution explicitly.
|
|
@@ -306,6 +313,8 @@ OpenTelemetry metrics are documented in [`docs/api/telemetry.md`](docs/api/telem
|
|
|
306
313
|
For automated pipelines, use the importable incremental API instead of re-fetching fixed date ranges:
|
|
307
314
|
|
|
308
315
|
```python
|
|
316
|
+
from datetime import datetime
|
|
317
|
+
|
|
309
318
|
from pdmt5 import Mt5Config
|
|
310
319
|
from mt5cli import update_history_with_config
|
|
311
320
|
from mt5cli.utils import Dataset
|
|
@@ -318,19 +327,17 @@ update_history_with_config(
|
|
|
318
327
|
datasets={Dataset.rates, Dataset.history_deals},
|
|
319
328
|
timeframes=["M1", "H1"], # default: all fixed MT5 timeframes
|
|
320
329
|
lookback_hours=24,
|
|
321
|
-
|
|
330
|
+
date_to=datetime(2024, 2, 1),
|
|
322
331
|
with_views=True,
|
|
323
332
|
include_account_events=True,
|
|
324
333
|
)
|
|
325
334
|
```
|
|
326
335
|
|
|
327
336
|
- **`collect-history`**: explicit date-range export into SQLite.
|
|
328
|
-
- **`update_history`**: incremental append based on existing SQLite `MAX(time)` per symbol (and timeframe for rates); account-level deals use a separate cursor when `include_account_events=True`.
|
|
337
|
+
- **`update_history`**: incremental append based on existing SQLite `MAX(time)` per symbol (and timeframe for rates); account-level deals use a separate cursor when `include_account_events=True`. Pass an explicit naive trade-server `date_to`; mt5cli cannot determine the current server time. Rates are persisted only in the canonical normalized `rates` table.
|
|
329
338
|
- **`rates` table**: normalized storage with `symbol` and `timeframe` columns.
|
|
330
|
-
- **
|
|
331
|
-
- **
|
|
332
|
-
- **Rate view loading**: use `load_rate_data()` / `load_rate_data_from_connection()` to load a SQLite rate table or view into a `DatetimeIndex` DataFrame.
|
|
333
|
-
- **Multi-series rate loading**: use `build_rate_targets()` to build neutral `RateTarget(symbol, timeframe)` pairs, `resolve_rate_tables()` to map them to table/view names (pass `require_existing=True` for strict resolution), and `load_rate_series_from_sqlite()` to load them into a mapping keyed by `(symbol, integer timeframe)`. The loader requires existing managed views unless `explicit_tables` is supplied, and rejects duplicate `(symbol, timeframe)` targets.
|
|
339
|
+
- **Explicit table loading**: use `load_rate_data()` / `load_rate_data_from_connection()` for intentionally named custom SQLite rate tables.
|
|
340
|
+
- **Multi-series rate loading**: use `build_rate_targets()` to build neutral `RateTarget(symbol, timeframe)` pairs and `load_rate_series_from_sqlite()` to load canonical `rates` rows into a mapping keyed by `(symbol, integer timeframe)`. Pass `explicit_tables` only for intentionally named custom tables; duplicate `(symbol, timeframe)` targets are rejected.
|
|
334
341
|
- **Multi-account latest rates**: use `collect_latest_rates_for_accounts()` with `AccountSpec` to read the latest bars for several account groups, merged into a `(symbol, integer timeframe)` mapping. For long-running pollers, `collect_latest_rates_for_accounts_with_retries()` adds bounded exponential backoff that retries only recoverable MT5 errors and re-raises once `retry_count` is exhausted.
|
|
335
342
|
- **Latest closed bars**: use `collect_latest_closed_rates_for_accounts()` when downstream logic must exclude the still-forming current bar. It fetches `count + 1` bars at `start_pos=0`, drops the last row with `drop_forming_rate_bar()`, and validates each series is non-empty. `collect_latest_closed_rates_by_granularity()` returns the same data keyed by `(symbol, granularity_name)` such as `("EURUSD", "M1")`.
|
|
336
343
|
|
|
@@ -347,7 +354,7 @@ eurusd_m1 = rates["EURUSD", "M1"] # closed bars only
|
|
|
347
354
|
```
|
|
348
355
|
|
|
349
356
|
- **Credential resolution**: use `resolve_account_spec()` / `resolve_account_specs()` to merge explicit override values over `AccountSpec` fields and expand `${ENV_VAR}` placeholders (via `substitute_env_placeholders()`), raising `ValueError` for missing variables. This keeps secrets out of plan/config files without coupling to any strategy code. For config dicts or nested structures loaded from YAML/TOML, use `substitute_mapping_values(data, keys={"login", "password"})` to expand placeholders only for caller-specified keys — key names are never hard-coded in mt5cli.
|
|
350
|
-
- **Throttled history updates**: use `ThrottledHistoryUpdater` to wrap `update_history()` with a minimum `interval_seconds` between successful runs (monotonic clock). Call `should_update()` / `update(client, symbols)` from an application loop; errors propagate by default, or pass `suppress_errors=True` to swallow recoverable `Mt5*Error`, `sqlite3.Error`, `ValueError`, and `OSError` without advancing the throttle (`AttributeError` / `TypeError` always propagate, since the client passed to `update()` must implement the canonical `HistoryClient` method names). Pass `update_backend` to inject a custom history update callable (same keyword arguments as `update_history`) instead of monkey-patching `mt5cli.history.update_history`.
|
|
357
|
+
- **Throttled history updates**: use `ThrottledHistoryUpdater` to wrap `update_history()` with a minimum `interval_seconds` between successful runs (monotonic clock). Call `should_update()` / `update(client, symbols, date_to=...)` from an application loop; errors propagate by default, or pass `suppress_errors=True` to swallow recoverable `Mt5*Error`, `sqlite3.Error`, `ValueError`, and `OSError` without advancing the throttle (`AttributeError` / `TypeError` always propagate, since the client passed to `update()` must implement the canonical `HistoryClient` method names). Pass `update_backend` to inject a custom history update callable (same keyword arguments as `update_history`) instead of monkey-patching `mt5cli.history.update_history`.
|
|
351
358
|
- **Trading helpers**: use `mt5_session()` for both market data and generic execution helpers. It owns initialization and shutdown for sessions it creates; caller-supplied clients remain caller-owned.
|
|
352
359
|
- **Granularity-keyed rate loading**: `load_rate_series_by_granularity()` builds targets with `build_rate_targets()`, loads them with `load_rate_series_from_sqlite()`, and returns a mapping keyed by `(symbol | None, granularity_name)` such as `("EURUSD", "M1")` to reduce downstream boilerplate.
|
|
353
360
|
- **MT5 session helper**: use the `mt5_session()` context manager to attach to (or, when `Mt5Config.path` is set, launch) an MT5 terminal, log in, and yield a connected `MT5Client` that shuts down on exit.
|
|
@@ -356,7 +363,6 @@ eurusd_m1 = rates["EURUSD", "M1"] # closed bars only
|
|
|
356
363
|
|
|
357
364
|
## Requirements
|
|
358
365
|
|
|
359
|
-
- Python 3.11+
|
|
360
366
|
- Windows OS (MetaTrader 5 requirement)
|
|
361
367
|
- MetaTrader 5 platform installed
|
|
362
368
|
|
|
@@ -406,7 +412,7 @@ updater = ThrottledHistoryUpdater(
|
|
|
406
412
|
output="history.db", interval_seconds=60, suppress_errors=True
|
|
407
413
|
)
|
|
408
414
|
with mt5_session(Mt5Config(login=login)) as client:
|
|
409
|
-
updater.update(client, ["EURUSD"])
|
|
415
|
+
updater.update(client, ["EURUSD"], date_to="2024-02-01")
|
|
410
416
|
```
|
|
411
417
|
|
|
412
418
|
Read-only collectors can keep using `mt5_session()` and `MT5Client`.
|