mt5cli 0.7.1__tar.gz → 0.8.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. mt5cli-0.8.0/.agents/skills/pr-feedback-triage/SKILL.md +149 -0
  2. {mt5cli-0.7.1 → mt5cli-0.8.0}/.github/workflows/ci.yml +15 -0
  3. mt5cli-0.8.0/AGENTS.md +37 -0
  4. {mt5cli-0.7.1 → mt5cli-0.8.0}/PKG-INFO +96 -7
  5. {mt5cli-0.7.1 → mt5cli-0.8.0}/README.md +94 -5
  6. mt5cli-0.8.0/docs/api/client.md +3 -0
  7. mt5cli-0.8.0/docs/api/converters.md +3 -0
  8. mt5cli-0.8.0/docs/api/exceptions.md +3 -0
  9. {mt5cli-0.7.1 → mt5cli-0.8.0}/docs/api/history.md +29 -3
  10. mt5cli-0.8.0/docs/api/index.md +53 -0
  11. mt5cli-0.8.0/docs/api/schemas.md +3 -0
  12. {mt5cli-0.7.1 → mt5cli-0.8.0}/docs/api/sdk.md +20 -5
  13. mt5cli-0.8.0/docs/api/storage.md +3 -0
  14. mt5cli-0.8.0/docs/api/trading.md +114 -0
  15. {mt5cli-0.7.1 → mt5cli-0.8.0}/docs/index.md +35 -33
  16. {mt5cli-0.7.1 → mt5cli-0.8.0}/mkdocs.yml +6 -1
  17. {mt5cli-0.7.1 → mt5cli-0.8.0}/mt5cli/__init__.py +102 -8
  18. {mt5cli-0.7.1 → mt5cli-0.8.0}/mt5cli/cli.py +6 -21
  19. mt5cli-0.8.0/mt5cli/client.py +88 -0
  20. mt5cli-0.8.0/mt5cli/converters.py +162 -0
  21. mt5cli-0.8.0/mt5cli/exceptions.py +90 -0
  22. {mt5cli-0.7.1 → mt5cli-0.8.0}/mt5cli/history.py +77 -14
  23. mt5cli-0.8.0/mt5cli/retry.py +64 -0
  24. mt5cli-0.8.0/mt5cli/schemas.py +291 -0
  25. {mt5cli-0.7.1 → mt5cli-0.8.0}/mt5cli/sdk.py +47 -35
  26. mt5cli-0.8.0/mt5cli/storage.py +49 -0
  27. mt5cli-0.8.0/mt5cli/trading.py +861 -0
  28. {mt5cli-0.7.1 → mt5cli-0.8.0}/mt5cli/utils.py +14 -0
  29. {mt5cli-0.7.1 → mt5cli-0.8.0}/pyproject.toml +2 -2
  30. mt5cli-0.8.0/tests/test_contracts.py +512 -0
  31. {mt5cli-0.7.1 → mt5cli-0.8.0}/tests/test_history.py +36 -0
  32. {mt5cli-0.7.1 → mt5cli-0.8.0}/tests/test_sdk.py +63 -6
  33. mt5cli-0.8.0/tests/test_trading.py +1270 -0
  34. {mt5cli-0.7.1 → mt5cli-0.8.0}/uv.lock +1 -1
  35. mt5cli-0.7.1/AGENTS.md +0 -81
  36. mt5cli-0.7.1/docs/api/index.md +0 -125
  37. mt5cli-0.7.1/docs/api/trading.md +0 -70
  38. mt5cli-0.7.1/mt5cli/trading.py +0 -210
  39. mt5cli-0.7.1/tests/test_trading.py +0 -356
  40. {mt5cli-0.7.1 → mt5cli-0.8.0}/.agents/skills/local-qa/SKILL.md +0 -0
  41. {mt5cli-0.7.1 → mt5cli-0.8.0}/.agents/skills/local-qa/scripts/qa.sh +0 -0
  42. {mt5cli-0.7.1 → mt5cli-0.8.0}/.agents/skills/mt5cli/SKILL.md +0 -0
  43. {mt5cli-0.7.1 → mt5cli-0.8.0}/.claude/agents/codex.md +0 -0
  44. {mt5cli-0.7.1 → mt5cli-0.8.0}/.claude/settings.json +0 -0
  45. {mt5cli-0.7.1 → mt5cli-0.8.0}/.github/FUNDING.yml +0 -0
  46. {mt5cli-0.7.1 → mt5cli-0.8.0}/.github/dependabot.yml +0 -0
  47. {mt5cli-0.7.1 → mt5cli-0.8.0}/.github/renovate.json +0 -0
  48. {mt5cli-0.7.1 → mt5cli-0.8.0}/.github/workflows/claude.yml +0 -0
  49. {mt5cli-0.7.1 → mt5cli-0.8.0}/.github/workflows/release.yml +0 -0
  50. {mt5cli-0.7.1 → mt5cli-0.8.0}/.gitignore +0 -0
  51. {mt5cli-0.7.1 → mt5cli-0.8.0}/CLAUDE.md +0 -0
  52. {mt5cli-0.7.1 → mt5cli-0.8.0}/LICENSE +0 -0
  53. {mt5cli-0.7.1 → mt5cli-0.8.0}/docs/api/cli.md +0 -0
  54. {mt5cli-0.7.1 → mt5cli-0.8.0}/docs/api/utils.md +0 -0
  55. {mt5cli-0.7.1 → mt5cli-0.8.0}/mt5cli/__main__.py +0 -0
  56. {mt5cli-0.7.1 → mt5cli-0.8.0}/tests/__init__.py +0 -0
  57. {mt5cli-0.7.1 → mt5cli-0.8.0}/tests/conftest.py +0 -0
  58. {mt5cli-0.7.1 → mt5cli-0.8.0}/tests/test_cli.py +0 -0
  59. {mt5cli-0.7.1 → mt5cli-0.8.0}/tests/test_utils.py +0 -0
@@ -0,0 +1,149 @@
1
+ ---
2
+ name: pr-feedback-triage
3
+ description: Triage pull request review comments into fixes, replies, clarification requests, or open follow-ups while respecting safe execution modes.
4
+ ---
5
+
6
+ # PR Feedback Triage
7
+
8
+ Triage pull request review feedback, decide what action each thread needs, make focused fixes when allowed, and report or resolve only what is actually handled.
9
+
10
+ ## When to Use
11
+
12
+ - A PR has review comments, requested changes, unresolved review threads, or bot review findings.
13
+ - The user asks to address, respond to, or resolve PR feedback.
14
+ - The user provides a PR URL/number, a branch with an associated PR, or copied comments.
15
+
16
+ Do not use this skill for a first-pass code review with no existing feedback; use a code review skill instead.
17
+
18
+ ## Inputs
19
+
20
+ - Pull request URL or number, or a current branch that has an associated pull request.
21
+ - Repository checkout or platform access sufficient to inspect the PR diff and review feedback.
22
+ - Optional reviewer priorities from the user, such as "only address blocking comments" or "do not reply on the PR platform".
23
+ - Optional operating mode flags: `dry_run`, `no_push`, and `no_reply`.
24
+
25
+ If no PR or review comments are identifiable, ask for the target PR or the copied comments before proceeding.
26
+
27
+ ## Modes
28
+
29
+ - `dry_run`: inspect review feedback and report the triage only. Do not edit files, run write-mode formatters, commit, push, post replies, or resolve review threads.
30
+ - `no_push`: local edits and verification are allowed, but do not push commits or otherwise update the remote branch. Report the local diff or local commits that still need to be pushed.
31
+ - `no_reply`: do not post replies, submit reviews, or resolve review threads. Provide suggested replies and resolution actions in the final report instead.
32
+
33
+ When a mode disables an action, skip that destructive or externally visible action even if normal workflow text would otherwise allow it.
34
+
35
+ ## Preflight
36
+
37
+ 1. Identify the current branch and target PR.
38
+ 2. Check tracked local changes with `git diff --name-only` and `git diff --cached --name-only`. Ignore untracked files unless the review feedback explicitly concerns them.
39
+ 3. Check unpushed commits before relying on remote review feedback.
40
+ 4. If tracked local changes or unpushed commits exist, warn that existing PR comments may not cover the latest local state. In `normal` mode, push only when the user request or repository workflow allows it; otherwise continue with a clearly reported limitation.
41
+
42
+ ## Feedback Collection
43
+
44
+ Gather the complete feedback set before editing:
45
+
46
+ - Fetch unresolved review threads, requested-change reviews, PR-level summary comments, and copied comments.
47
+ - Use platform-native APIs/CLI when available. Paginate results; do not inspect only the first page of threads or comments.
48
+ - For bot reviewers that post both summary comments and inline comments, collect both. Summary comments often contain severity, rationale, and fix instructions; inline comments contain the exact file and line context.
49
+ - Preserve each thread/comment identifier needed to reply or resolve later.
50
+ - Compare each comment with the current diff and file contents because review lines can become outdated.
51
+
52
+ ## Deduplication and Ordering
53
+
54
+ Build one triage record per distinct finding:
55
+
56
+ - Prefer exact review-thread identity when available.
57
+ - For duplicate bot findings appearing in both summary and inline comments, merge by exact issue title first, then by file path plus line range as a fallback.
58
+ - Prefer inline comments for location and current code context.
59
+ - Prefer summary comments for severity, category, rationale, and detailed agent prompts.
60
+ - Preserve the reviewer’s exact issue title and original wording where practical. Do not rename findings in a way that would make replies hard to map back to comments.
61
+ - Preserve the reviewer’s original ordering unless the user asks for priority reordering. Many review bots already order findings by severity.
62
+
63
+ Each triage record should track: original title, reviewer, source IDs, location, current applicability, severity/priority if available, disposition, planned action, verification, reply text if any, and resolution decision.
64
+
65
+ ## Resolution Policy
66
+
67
+ In normal mode, `Resolve conversation` is the default action for any review thread that has been fully handled. A thread is handled when the requested change is implemented and verified, the current code already satisfies the comment, the comment is outdated and no longer applies, or a deliberate deferral/won't-fix response has been posted with a clear reason.
68
+
69
+ Keep a thread open only when it still needs reviewer, maintainer, or product input, the fix is local-only and not pushed, verification is missing for a material change, or the user explicitly requested `dry_run`, `no_push`, or `no_reply` behavior that prevents resolution.
70
+
71
+ When resolving a thread, add a concise reply first only if it provides useful context, such as what changed, why no code change was needed, why a finding was intentionally deferred, or why the original comment is now outdated. Do not add noisy replies for self-evident fixes unless project norms require them.
72
+
73
+ ## Flow
74
+
75
+ ```mermaid
76
+ flowchart TD
77
+ A[Identify PR and branch state] --> B[Collect all review feedback]
78
+ B --> C[Deduplicate and preserve source IDs]
79
+ C --> D[Inspect current diff and code]
80
+ D --> E{Classify each triage record}
81
+ E -->|Fix| F[Implement minimal change]
82
+ E -->|Answer| G[Prepare concise reply]
83
+ E -->|Clarify| H[Leave open with question]
84
+ E -->|Already addressed or Outdated| I[Prepare evidence]
85
+ E -->|Defer or Won't fix| J[Document reason]
86
+ F --> K[Verify]
87
+ G --> L{Mode}
88
+ H --> L
89
+ I --> L
90
+ J --> L
91
+ K --> L
92
+ L -->|dry_run| M[Report triage only]
93
+ L -->|no_push| N[Report local diff or commits]
94
+ L -->|no_reply| O[Report suggested replies/actions]
95
+ L -->|normal| P[Commit/push if changed, then batch reply and resolve handled threads]
96
+ M --> Q[Final summary]
97
+ N --> Q
98
+ O --> Q
99
+ P --> Q
100
+ ```
101
+
102
+ ## Compact Workflow
103
+
104
+ 1. **Collect all relevant feedback**
105
+ - Identify the PR and gather unresolved review threads, requested-change reviews, PR-level summaries, inline comments, and copied comments.
106
+ - Paginate all platform calls and keep comment/thread IDs for later replies and resolution.
107
+ - For bot reviews, collect both summary and inline comments, then merge duplicates rather than fixing the same finding twice.
108
+
109
+ 2. **Classify each triage record**
110
+ - **Fix**: Valid requested change; make the smallest focused edit when not in `dry_run`.
111
+ - **Answer**: No code change needed; prepare a concise explanation.
112
+ - **Clarify**: Ambiguous, conflicting, or missing context; leave open with a question.
113
+ - **Already addressed**: Current code already satisfies it; prepare evidence.
114
+ - **Outdated**: Commented code or issue no longer exists; prepare evidence.
115
+ - **Defer / Won't fix**: Valid concern intentionally not changed now; document a specific reason.
116
+
117
+ 3. **Act according to the classification and mode**
118
+ - Keep edits scoped to the review feedback.
119
+ - Follow reviewer-provided fix instructions literally when they are still applicable; deviate only when the current code proves the instruction is stale or unsafe.
120
+ - In `dry_run`, stop at triage, proposed fixes, suggested replies, and verification plan.
121
+ - In `no_push`, local edits are allowed, but do not push or resolve threads for local-only fixes.
122
+ - In `no_reply`, do not post replies or resolve threads; report suggested replies/actions instead.
123
+ - In normal mode, batch replies where practical, then resolve every handled thread by default after the fix, explanation, or deferral reason is available on the PR.
124
+
125
+ 4. **Verify before claiming completion**
126
+ - For fixes, run appropriate checks or explain why they could not run.
127
+ - Re-inspect the updated diff and comment context to confirm the concern is resolved.
128
+ - Do not mark a thread resolved if it still needs reviewer, maintainer, or product input.
129
+
130
+ 5. **Finish**
131
+ - Normal mode: commit/push changes when appropriate, post useful replies or a summary, and resolve all handled threads by default.
132
+ - Safe modes: report the local state and the exact replies/resolution actions a human could take.
133
+
134
+ ## Reply Guidance
135
+
136
+ - Keep inline replies short and tied to the original title or concern.
137
+ - For fixed findings, mention the concrete change or commit if useful.
138
+ - For already-addressed or outdated findings, cite the current code path or behavior that makes the finding no longer applicable.
139
+ - For deferred or won't-fix findings, provide the reason and any follow-up issue or owner if known.
140
+ - If a reply or resolve operation fails, continue with the remaining threads and report the failure in the final summary.
141
+
142
+ ## Final Summary Checklist
143
+
144
+ - Mode used: `normal`, `dry_run`, `no_push`, or `no_reply`
145
+ - Counts by disposition: fixed, answered, clarified/left open, already addressed, outdated, deferred/won't-fix
146
+ - Threads resolved, intentionally left open, or resolution actions skipped by mode
147
+ - Verification run or planned
148
+ - Commits pushed, local diff/commits, or "none"
149
+ - Remaining open items and who needs to respond
@@ -62,6 +62,19 @@ jobs:
62
62
  runs-on: ubuntu-slim
63
63
  secrets:
64
64
  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
65
+ github-codeql-analysis:
66
+ if: >
67
+ github.event_name == 'push'
68
+ || github.event_name == 'pull_request'
69
+ || (github.event_name == 'workflow_dispatch' && inputs.workflow == 'lint-and-test')
70
+ permissions:
71
+ contents: read
72
+ security-events: write
73
+ actions: read
74
+ uses: dceoy/gh-actions-for-devops/.github/workflows/github-codeql-analysis.yml@main # zizmor: ignore[unpinned-uses]
75
+ with:
76
+ language: >
77
+ ["python"]
65
78
  dependabot-auto-merge:
66
79
  if: >
67
80
  github.event_name == 'pull_request' && github.actor == 'dependabot[bot]'
@@ -73,5 +86,7 @@ jobs:
73
86
  contents: write
74
87
  pull-requests: write
75
88
  actions: read
89
+ checks: read
90
+ statuses: read
76
91
  with:
77
92
  unconditional: true
mt5cli-0.8.0/AGENTS.md ADDED
@@ -0,0 +1,37 @@
1
+ # Repository Guidelines
2
+
3
+ ## Project Structure & Module Organization
4
+
5
+ `mt5cli/` contains the package source. Important modules include `cli.py` for the Typer command-line app, `client.py` and `sdk.py` for public MT5 client/session APIs, `history.py` for SQLite history collection, `storage.py` and `converters.py` for export behavior, and `schemas.py` for normalized dataset contracts. `tests/` holds pytest coverage for CLI behavior, SDK contracts, trading helpers, history, and utilities. `docs/` and `mkdocs.yml` define the MkDocs site and API reference. `skills/mt5cli/SKILL.md` documents the mt5cli agent skill.
6
+
7
+ ## Build, Test, and Development Commands
8
+
9
+ - `uv sync` installs runtime and development dependencies from `pyproject.toml` and `uv.lock`.
10
+ - `uv run mt5cli --help` runs the local CLI entry point.
11
+ - `uv run ruff format .` formats Python files.
12
+ - `uv run ruff check --fix .` lints and applies safe fixes.
13
+ - `uv run pyright .` runs strict type checking.
14
+ - `uv run pytest` runs doctests, branch coverage, and the test suite.
15
+ - `uv run mkdocs serve` previews documentation locally; `uv run mkdocs build` validates the docs build.
16
+
17
+ 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.
18
+
19
+ ## Coding Style & Naming Conventions
20
+
21
+ Target Python `>=3.11,<3.14`. Use Ruff’s configured 88-character line length and Google-style docstrings. Pyright is strict, so prefer explicit public type annotations and narrow exception handling. Keep module, function, and variable names in `snake_case`; classes and enums use `PascalCase`. Preserve the package’s small, typed helper style rather than adding broad abstractions.
22
+
23
+ ## Design Principles
24
+
25
+ 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.
26
+
27
+ ## Testing Guidelines
28
+
29
+ Tests use pytest, pytest-mock, doctests, and pytest-cov. Test files should match `tests/test_*.py`, classes `Test*`, and functions `test_*`. Coverage is configured with `fail_under = 100`, so add focused tests for every behavior change. Mock MT5/pdmt5 boundaries; do not require a live MetaTrader terminal in unit tests.
30
+
31
+ ## Commit & Pull Request Guidelines
32
+
33
+ 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.
34
+
35
+ ## Security & Configuration Tips
36
+
37
+ 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,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mt5cli
3
- Version: 0.7.1
4
- Summary: Command-line tool for MetaTrader 5
3
+ Version: 0.8.0
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>
7
7
  Maintainer-email: dceoy <dceoy@users.noreply.github.com>
@@ -26,14 +26,14 @@ Description-Content-Type: text/markdown
26
26
 
27
27
  [![CI/CD](https://github.com/dceoy/mt5cli/actions/workflows/ci.yml/badge.svg)](https://github.com/dceoy/mt5cli/actions/workflows/ci.yml)
28
28
 
29
- Command-line tool for exporting MetaTrader 5 data to CSV, JSON, Parquet, and SQLite3.
29
+ Generic MT5 data and execution infrastructure for Python applications. Export from the CLI or import a small, stable Python API in downstream packages.
30
30
 
31
31
  Built on top of [pdmt5](https://github.com/dceoy/pdmt5), a pandas-based data handler for MetaTrader 5.
32
32
 
33
33
  ## Architecture
34
34
 
35
35
  - **pdmt5** — canonical MT5 client, DataFrame/trading primitives, and MT5 constant parsing (`TIMEFRAME_*`, `COPY_TICKS_*`, order types).
36
- - **mt5cli** — CLI commands, CSV/JSON/Parquet/SQLite export, SQLite history collection, rate views, and local batch/automation SDK helpers built on pdmt5.
36
+ - **mt5cli** — public `MT5Client` API, standardized dataset schemas, storage helpers, CLI commands, and SQLite history collection built on pdmt5.
37
37
  - **mt5api** — sibling HTTP adapter for remote MT5 access; not a dependency of mt5cli.
38
38
 
39
39
  ## Features
@@ -51,7 +51,96 @@ Built on top of [pdmt5](https://github.com/dceoy/pdmt5), a pandas-based data han
51
51
  pip install -U mt5cli MetaTrader5
52
52
  ```
53
53
 
54
- ## Usage
54
+ ## Python API (downstream packages)
55
+
56
+ Import `MT5Client` for generic MT5 data access, schema normalization, and optional order primitives. `Mt5CliClient` remains available as a backward-compatible alias.
57
+
58
+ ```python
59
+ from datetime import UTC, datetime
60
+ from pathlib import Path
61
+
62
+ from mt5cli import (
63
+ DataKind,
64
+ Dataset,
65
+ MT5Client,
66
+ build_config,
67
+ collect_history,
68
+ export_dataframe,
69
+ mt5_session,
70
+ normalize_dataframe,
71
+ update_history_with_config,
72
+ )
73
+
74
+ # Persistent session for multiple calls
75
+ with mt5_session(build_config(login=12345, server="Broker-Demo")) as client:
76
+ rates = client.copy_rates_range(
77
+ "EURUSD",
78
+ timeframe="H1",
79
+ date_from="2024-01-01",
80
+ date_to="2024-02-01",
81
+ )
82
+ positions = client.positions()
83
+ check = client.order_check({"action": 1, "symbol": "EURUSD", "volume": 0.1})
84
+
85
+ # Normalize MT5 frames to the public schema contract before storage
86
+ closed_rates = normalize_dataframe(
87
+ rates, DataKind.rates, symbol="EURUSD", timeframe="H1"
88
+ )
89
+ export_dataframe(closed_rates, Path("rates.csv"), "csv")
90
+
91
+ # Bulk SQLite history (same behavior as collect-history CLI command)
92
+ collect_history(
93
+ Path("history.db"),
94
+ symbols=["EURUSD"],
95
+ date_from=datetime(2024, 1, 1, tzinfo=UTC),
96
+ date_to=datetime(2024, 2, 1, tzinfo=UTC),
97
+ datasets={Dataset.rates, Dataset.history_deals},
98
+ )
99
+
100
+ # Incremental append for automated pipelines
101
+ update_history_with_config(
102
+ output="history.db",
103
+ symbols=["EURUSD"],
104
+ config=build_config(login=12345),
105
+ )
106
+ ```
107
+
108
+ Schema contracts live in `mt5cli.schemas` (`DataKind`, `validate_schema`, `normalize_dataframe`). Storage helpers are re-exported from `mt5cli.storage` and the package root.
109
+
110
+ `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.
111
+
112
+ ### Trading lifecycle and state helpers
113
+
114
+ Trading applications can depend on `mt5cli` imports only; terminal path,
115
+ credentials, server, and timeout are forwarded to `pdmt5.Mt5Config`, numeric
116
+ login strings are coerced to integers, and empty login strings are treated as
117
+ unset.
118
+
119
+ ```python
120
+ from mt5cli import (
121
+ calculate_spread_ratio,
122
+ create_trading_client,
123
+ get_account_snapshot,
124
+ mt5_trading_session,
125
+ )
126
+
127
+ with mt5_trading_session(
128
+ path=r"C:\Program Files\MetaTrader 5\terminal64.exe",
129
+ login="12345",
130
+ password="from-env-or-secret-store",
131
+ server="Broker-Demo",
132
+ ) as client:
133
+ account = get_account_snapshot(client)
134
+ spread = calculate_spread_ratio(client, "EURUSD")
135
+
136
+ client = create_trading_client(login=12345, server="Broker-Demo")
137
+ try:
138
+ positions = client.positions_get_as_df(symbol="EURUSD")
139
+ finally:
140
+ client.shutdown()
141
+ ```
142
+
143
+ ## CLI usage
55
144
 
56
145
  ```bash
57
146
  # Export account information to CSV
@@ -185,7 +274,7 @@ eurusd_m1 = rates["EURUSD", "M1"] # closed bars only
185
274
  - **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`, `OSError`, and MT5 client capability errors for history API methods without advancing the throttle (other `AttributeError` / `TypeError` values always propagate).
186
275
  - **Trading session helpers**: use `mt5_trading_session()` for a trading-capable `pdmt5.Mt5TradingClient` that initializes/logs in via `Mt5Config.path` and always shuts down safely. Pair with `detect_position_side()`, `calculate_margin_and_volume()`, and `determine_order_limits()` for generic position and sizing utilities. The read-only `mt5_session()` / `Mt5CliClient` SDK is unchanged.
187
276
  - **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.
188
- - **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 `Mt5CliClient` that shuts down on exit.
277
+ - **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.
189
278
  - **SQLite export helpers**: use `export_dataframe_to_sqlite()` for append mode, optional index export, and post-write deduplication by key columns.
190
279
  - **Recent ticks and margins**: `recent_ticks()` and `minimum_margins()` SDK helpers (and matching CLI commands) cover common downstream read-only queries.
191
280
 
@@ -250,7 +339,7 @@ finally:
250
339
  client.shutdown()
251
340
  ```
252
341
 
253
- Read-only collectors can keep using `mt5_session()` and `Mt5CliClient` without changes.
342
+ Read-only collectors can keep using `mt5_session()` and `MT5Client` (or the `Mt5CliClient` alias) without changes.
254
343
 
255
344
  ## Development
256
345
 
@@ -2,14 +2,14 @@
2
2
 
3
3
  [![CI/CD](https://github.com/dceoy/mt5cli/actions/workflows/ci.yml/badge.svg)](https://github.com/dceoy/mt5cli/actions/workflows/ci.yml)
4
4
 
5
- Command-line tool for exporting MetaTrader 5 data to CSV, JSON, Parquet, and SQLite3.
5
+ Generic MT5 data and execution infrastructure for Python applications. Export from the CLI or import a small, stable Python API in downstream packages.
6
6
 
7
7
  Built on top of [pdmt5](https://github.com/dceoy/pdmt5), a pandas-based data handler for MetaTrader 5.
8
8
 
9
9
  ## Architecture
10
10
 
11
11
  - **pdmt5** — canonical MT5 client, DataFrame/trading primitives, and MT5 constant parsing (`TIMEFRAME_*`, `COPY_TICKS_*`, order types).
12
- - **mt5cli** — CLI commands, CSV/JSON/Parquet/SQLite export, SQLite history collection, rate views, and local batch/automation SDK helpers built on pdmt5.
12
+ - **mt5cli** — public `MT5Client` API, standardized dataset schemas, storage helpers, CLI commands, and SQLite history collection built on pdmt5.
13
13
  - **mt5api** — sibling HTTP adapter for remote MT5 access; not a dependency of mt5cli.
14
14
 
15
15
  ## Features
@@ -27,7 +27,96 @@ Built on top of [pdmt5](https://github.com/dceoy/pdmt5), a pandas-based data han
27
27
  pip install -U mt5cli MetaTrader5
28
28
  ```
29
29
 
30
- ## Usage
30
+ ## Python API (downstream packages)
31
+
32
+ Import `MT5Client` for generic MT5 data access, schema normalization, and optional order primitives. `Mt5CliClient` remains available as a backward-compatible alias.
33
+
34
+ ```python
35
+ from datetime import UTC, datetime
36
+ from pathlib import Path
37
+
38
+ from mt5cli import (
39
+ DataKind,
40
+ Dataset,
41
+ MT5Client,
42
+ build_config,
43
+ collect_history,
44
+ export_dataframe,
45
+ mt5_session,
46
+ normalize_dataframe,
47
+ update_history_with_config,
48
+ )
49
+
50
+ # Persistent session for multiple calls
51
+ with mt5_session(build_config(login=12345, server="Broker-Demo")) as client:
52
+ rates = client.copy_rates_range(
53
+ "EURUSD",
54
+ timeframe="H1",
55
+ date_from="2024-01-01",
56
+ date_to="2024-02-01",
57
+ )
58
+ positions = client.positions()
59
+ check = client.order_check({"action": 1, "symbol": "EURUSD", "volume": 0.1})
60
+
61
+ # Normalize MT5 frames to the public schema contract before storage
62
+ closed_rates = normalize_dataframe(
63
+ rates, DataKind.rates, symbol="EURUSD", timeframe="H1"
64
+ )
65
+ export_dataframe(closed_rates, Path("rates.csv"), "csv")
66
+
67
+ # Bulk SQLite history (same behavior as collect-history CLI command)
68
+ collect_history(
69
+ Path("history.db"),
70
+ symbols=["EURUSD"],
71
+ date_from=datetime(2024, 1, 1, tzinfo=UTC),
72
+ date_to=datetime(2024, 2, 1, tzinfo=UTC),
73
+ datasets={Dataset.rates, Dataset.history_deals},
74
+ )
75
+
76
+ # Incremental append for automated pipelines
77
+ update_history_with_config(
78
+ output="history.db",
79
+ symbols=["EURUSD"],
80
+ config=build_config(login=12345),
81
+ )
82
+ ```
83
+
84
+ Schema contracts live in `mt5cli.schemas` (`DataKind`, `validate_schema`, `normalize_dataframe`). Storage helpers are re-exported from `mt5cli.storage` and the package root.
85
+
86
+ `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.
87
+
88
+ ### Trading lifecycle and state helpers
89
+
90
+ Trading applications can depend on `mt5cli` imports only; terminal path,
91
+ credentials, server, and timeout are forwarded to `pdmt5.Mt5Config`, numeric
92
+ login strings are coerced to integers, and empty login strings are treated as
93
+ unset.
94
+
95
+ ```python
96
+ from mt5cli import (
97
+ calculate_spread_ratio,
98
+ create_trading_client,
99
+ get_account_snapshot,
100
+ mt5_trading_session,
101
+ )
102
+
103
+ with mt5_trading_session(
104
+ path=r"C:\Program Files\MetaTrader 5\terminal64.exe",
105
+ login="12345",
106
+ password="from-env-or-secret-store",
107
+ server="Broker-Demo",
108
+ ) as client:
109
+ account = get_account_snapshot(client)
110
+ spread = calculate_spread_ratio(client, "EURUSD")
111
+
112
+ client = create_trading_client(login=12345, server="Broker-Demo")
113
+ try:
114
+ positions = client.positions_get_as_df(symbol="EURUSD")
115
+ finally:
116
+ client.shutdown()
117
+ ```
118
+
119
+ ## CLI usage
31
120
 
32
121
  ```bash
33
122
  # Export account information to CSV
@@ -161,7 +250,7 @@ eurusd_m1 = rates["EURUSD", "M1"] # closed bars only
161
250
  - **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`, `OSError`, and MT5 client capability errors for history API methods without advancing the throttle (other `AttributeError` / `TypeError` values always propagate).
162
251
  - **Trading session helpers**: use `mt5_trading_session()` for a trading-capable `pdmt5.Mt5TradingClient` that initializes/logs in via `Mt5Config.path` and always shuts down safely. Pair with `detect_position_side()`, `calculate_margin_and_volume()`, and `determine_order_limits()` for generic position and sizing utilities. The read-only `mt5_session()` / `Mt5CliClient` SDK is unchanged.
163
252
  - **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.
164
- - **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 `Mt5CliClient` that shuts down on exit.
253
+ - **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.
165
254
  - **SQLite export helpers**: use `export_dataframe_to_sqlite()` for append mode, optional index export, and post-write deduplication by key columns.
166
255
  - **Recent ticks and margins**: `recent_ticks()` and `minimum_margins()` SDK helpers (and matching CLI commands) cover common downstream read-only queries.
167
256
 
@@ -226,7 +315,7 @@ finally:
226
315
  client.shutdown()
227
316
  ```
228
317
 
229
- Read-only collectors can keep using `mt5_session()` and `Mt5CliClient` without changes.
318
+ Read-only collectors can keep using `mt5_session()` and `MT5Client` (or the `Mt5CliClient` alias) without changes.
230
319
 
231
320
  ## Development
232
321
 
@@ -0,0 +1,3 @@
1
+ # Client
2
+
3
+ ::: mt5cli.client
@@ -0,0 +1,3 @@
1
+ # Converters
2
+
3
+ ::: mt5cli.converters
@@ -0,0 +1,3 @@
1
+ # Exceptions
2
+
3
+ ::: mt5cli.exceptions
@@ -167,19 +167,45 @@ Resolution rules:
167
167
 
168
168
  ### Rate data loading
169
169
 
170
- Use `load_rate_data()` to load a table or view from a SQLite path, or
171
- `load_rate_data_from_connection()` when you already have a connection:
170
+ The canonical normalized rate table is `rates`; compatibility views are named
171
+ with `rate_<symbol>__<timeframe>` for single-timeframe symbols or
172
+ `rate_<symbol>__<granularity>_<timeframe>` when a symbol has multiple stored
173
+ timeframes. `resolve_rate_table_name()` returns `rates`, while
174
+ `resolve_rate_view_name()` returns the per-symbol compatibility view name.
175
+
176
+ Use `load_rate_data()` or `load_rate_series_from_sqlite(..., table=...)` to load
177
+ a single table or view from a SQLite path. Use
178
+ `load_rate_series_by_granularity()` to load multiple instrument/granularity
179
+ targets without hard-coding view names:
172
180
 
173
181
  ```python
174
182
  from pathlib import Path
175
183
 
176
- from mt5cli import load_rate_data
184
+ from mt5cli import (
185
+ load_rate_data,
186
+ load_rate_series_by_granularity,
187
+ load_rate_series_from_sqlite,
188
+ resolve_rate_table_name,
189
+ )
177
190
  from mt5cli.history import resolve_rate_view_name
178
191
 
179
192
  view = resolve_rate_view_name(Path("history.db"), "EURUSD", "M1", require_existing=True)
180
193
  rates = load_rate_data(Path("history.db"), view, count=1000)
194
+ same_rates = load_rate_series_from_sqlite(Path("history.db"), table=view, count=1000)
195
+
196
+ table = resolve_rate_table_name("EURUSD", "M1") # "rates"
197
+ series = load_rate_series_by_granularity(
198
+ Path("history.db"),
199
+ symbols=["EURUSD", "GBPUSD"],
200
+ granularities=["M1", "H1"],
201
+ count=500,
202
+ )
181
203
  ```
182
204
 
205
+ `count` returns the latest rows while preserving chronological order. Missing
206
+ tables/views and mismatched `explicit_tables` lengths raise `ValueError` with
207
+ the requested database target in the message.
208
+
183
209
  The loader accepts close-based OHLC rate data or tick-like bid/ask data. It
184
210
  validates that `time` exists, parses timestamps with pandas, and returns a
185
211
  DataFrame indexed by ascending `DatetimeIndex` named `time`.
@@ -0,0 +1,53 @@
1
+ # API Reference
2
+
3
+ This section documents the mt5cli public Python API and CLI modules.
4
+
5
+ ## Public API layers
6
+
7
+ | Module | Purpose |
8
+ | ----------------------------------------- | ------------------------------------------------------------------------- |
9
+ | [Client](client.md) | `MT5Client` session abstraction for data access and order primitives |
10
+ | [Schemas](schemas.md) | Canonical DataFrame contracts and normalization helpers |
11
+ | [Storage](storage.md) | CSV/JSON/Parquet/SQLite export and history collection helpers |
12
+ | [Converters](converters.md) | Symbol, timeframe, timezone, and date-range utilities |
13
+ | [Exceptions](exceptions.md) | Stable mt5cli exception types and MT5 error normalization |
14
+ | [SDK](sdk.md) | Module-level fetch helpers, multi-account collectors, incremental history |
15
+ | [Trading](trading.md) | Trading-capable sessions and operational helpers |
16
+ | [History Collection (SQLite)](history.md) | SQLite schema, incremental writes, dedup, and rate views |
17
+ | [CLI](cli.md) | Typer commands that delegate to the Python API |
18
+ | [Utils](utils.md) | Parsing helpers and Click parameter types |
19
+
20
+ ## Architecture overview
21
+
22
+ ```mermaid
23
+ flowchart TD
24
+ App["Downstream application"] --> Client["MT5Client"]
25
+ CLI["mt5cli CLI"] --> Client
26
+ Client --> SDK["sdk / pdmt5"]
27
+ Client --> Schemas["schemas"]
28
+ Storage["storage"] --> History["history SQLite"]
29
+ Storage --> Utils["utils export"]
30
+ SDK --> PDMT5["pdmt5.Mt5DataClient"]
31
+ ```
32
+
33
+ Downstream packages should depend on the package root exports (`MT5Client`, `DataKind`, `normalize_dataframe`, `export_dataframe`, `collect_history`, etc.) rather than private modules.
34
+
35
+ `MT5Client.order_send()` is a live execution primitive that can place real trades. mt5cli exposes minimal execution helpers only; strategy logic, signals, backtests, and optimization remain out of scope and must be implemented downstream with explicit execution gating.
36
+
37
+ ## Quick start
38
+
39
+ ```python
40
+ from mt5cli import MT5Client, build_config, mt5_session
41
+
42
+ with mt5_session(build_config(login=12345)) as client:
43
+ rates = client.copy_rates_range("EURUSD", "H1", "2024-01-01", "2024-02-01")
44
+ positions = client.positions()
45
+ ```
46
+
47
+ ```bash
48
+ mt5cli -o account.csv account-info
49
+ mt5cli -o rates.parquet rates-range --symbol EURUSD --timeframe H1 \
50
+ --date-from 2024-01-01 --date-to 2024-02-01
51
+ ```
52
+
53
+ See individual module pages for detailed usage examples.
@@ -0,0 +1,3 @@
1
+ # Schemas
2
+
3
+ ::: mt5cli.schemas
@@ -31,13 +31,25 @@ rates = collect_latest_rates_for_accounts_with_retries(
31
31
  ### Latest closed rate bars
32
32
 
33
33
  MetaTrader 5 `start_pos=0` includes the still-forming current bar as the last
34
- row. `collect_latest_closed_rates_for_accounts()` fetches `count + 1` bars,
35
- drops that row with `drop_forming_rate_bar()`, and validates each series is
36
- non-empty. Use `collect_latest_closed_rates_by_granularity()` when callers
37
- prefer keys such as `("EURUSD", "M1")` instead of integer timeframes.
34
+ row. `fetch_latest_closed_rates()` handles one connected client; multi-account
35
+ helpers fetch `count + 1` bars, drop that row with `drop_forming_rate_bar()`,
36
+ and validate each series is non-empty. Returned frames are ordered
37
+ oldest-to-newest and may contain fewer than `count` rows only when MT5 returns
38
+ fewer closed bars.
38
39
 
39
40
  ```python
40
- from mt5cli import AccountSpec, collect_latest_closed_rates_by_granularity
41
+ from mt5cli import (
42
+ AccountSpec,
43
+ collect_latest_closed_rates_by_granularity,
44
+ fetch_latest_closed_rates,
45
+ )
46
+
47
+ closed = fetch_latest_closed_rates(
48
+ client,
49
+ symbol="EURUSD",
50
+ granularity="M1",
51
+ count=500,
52
+ )
41
53
 
42
54
  rates = collect_latest_closed_rates_by_granularity(
43
55
  [AccountSpec(symbols=["EURUSD"], login=12345)],
@@ -48,6 +60,9 @@ rates = collect_latest_closed_rates_by_granularity(
48
60
  closed_m1 = rates["EURUSD", "M1"]
49
61
  ```
50
62
 
63
+ Use `collect_latest_closed_rates_by_granularity()` when callers prefer keys such
64
+ as `("EURUSD", "M1")` instead of integer timeframes.
65
+
51
66
  ### Resolving credentials and `${ENV_VAR}` placeholders
52
67
 
53
68
  `resolve_account_spec()` / `resolve_account_specs()` merge explicit override