mt5cli 0.8.1__tar.gz → 0.8.3__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-0.8.1 → mt5cli-0.8.3}/.agents/skills/pr-feedback-triage/SKILL.md +63 -11
- {mt5cli-0.8.1 → mt5cli-0.8.3}/PKG-INFO +2 -2
- {mt5cli-0.8.1 → mt5cli-0.8.3}/README.md +1 -1
- {mt5cli-0.8.1 → mt5cli-0.8.3}/docs/api/public-contract.md +12 -10
- {mt5cli-0.8.1 → mt5cli-0.8.3}/docs/api/sdk.md +26 -3
- {mt5cli-0.8.1 → mt5cli-0.8.3}/docs/api/trading.md +29 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/mt5cli/__init__.py +8 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/mt5cli/contract.py +4 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/mt5cli/sdk.py +23 -5
- {mt5cli-0.8.1 → mt5cli-0.8.3}/mt5cli/trading.py +206 -1
- {mt5cli-0.8.1 → mt5cli-0.8.3}/pyproject.toml +1 -1
- {mt5cli-0.8.1 → mt5cli-0.8.3}/tests/test_contracts.py +42 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/tests/test_sdk.py +151 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/tests/test_trading.py +699 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/uv.lock +1 -1
- {mt5cli-0.8.1 → mt5cli-0.8.3}/.agents/skills/local-qa/SKILL.md +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/.agents/skills/local-qa/scripts/qa.sh +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/.agents/skills/mt5cli/SKILL.md +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/.claude/agents/codex.md +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/.claude/settings.json +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/.github/FUNDING.yml +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/.github/dependabot.yml +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/.github/renovate.json +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/.github/workflows/ci.yml +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/.github/workflows/claude.yml +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/.github/workflows/release.yml +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/.gitignore +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/AGENTS.md +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/CLAUDE.md +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/LICENSE +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/docs/api/cli.md +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/docs/api/client.md +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/docs/api/converters.md +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/docs/api/exceptions.md +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/docs/api/history.md +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/docs/api/index.md +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/docs/api/schemas.md +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/docs/api/storage.md +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/docs/api/utils.md +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/docs/index.md +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/mkdocs.yml +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/mt5cli/__main__.py +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/mt5cli/cli.py +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/mt5cli/client.py +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/mt5cli/converters.py +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/mt5cli/exceptions.py +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/mt5cli/history.py +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/mt5cli/retry.py +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/mt5cli/schemas.py +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/mt5cli/storage.py +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/mt5cli/utils.py +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/tests/__init__.py +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/tests/conftest.py +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/tests/test_cli.py +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/tests/test_history.py +0 -0
- {mt5cli-0.8.1 → mt5cli-0.8.3}/tests/test_utils.py +0 -0
|
@@ -27,7 +27,7 @@ If no PR or review comments are identifiable, ask for the target PR or the copie
|
|
|
27
27
|
## Modes
|
|
28
28
|
|
|
29
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.
|
|
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. Do not resolve threads whose resolution depends on unpushed local edits.
|
|
31
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
32
|
|
|
33
33
|
When a mode disables an action, skip that destructive or externally visible action even if normal workflow text would otherwise allow it.
|
|
@@ -46,7 +46,7 @@ Gather the complete feedback set before editing:
|
|
|
46
46
|
- Fetch unresolved review threads, requested-change reviews, PR-level summary comments, and copied comments.
|
|
47
47
|
- Use platform-native APIs/CLI when available. Paginate results; do not inspect only the first page of threads or comments.
|
|
48
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
|
|
49
|
+
- Preserve every thread/comment identifier needed to reply or resolve later.
|
|
50
50
|
- Compare each comment with the current diff and file contents because review lines can become outdated.
|
|
51
51
|
|
|
52
52
|
## Deduplication and Ordering
|
|
@@ -60,7 +60,7 @@ Build one triage record per distinct finding:
|
|
|
60
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
61
|
- Preserve the reviewer’s original ordering unless the user asks for priority reordering. Many review bots already order findings by severity.
|
|
62
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
|
|
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, resolution decision, platform action attempted, and final platform state.
|
|
64
64
|
|
|
65
65
|
## Resolution Policy
|
|
66
66
|
|
|
@@ -70,6 +70,52 @@ Keep a thread open only when it still needs reviewer, maintainer, or product inp
|
|
|
70
70
|
|
|
71
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
72
|
|
|
73
|
+
## Platform Action Contract
|
|
74
|
+
|
|
75
|
+
Do not treat triage as complete until every collected source ID reaches an explicit terminal state:
|
|
76
|
+
|
|
77
|
+
- `resolved`: a platform resolve action succeeded, or a re-check shows the thread is already resolved.
|
|
78
|
+
- `replied_left_open`: a reply or question was posted and the thread is intentionally left unresolved.
|
|
79
|
+
- `not_resolvable`: the source is a PR-level summary comment or copied comment that has no platform-level resolve action; reply or post a PR summary when useful.
|
|
80
|
+
- `skipped_by_mode`: `dry_run`, `no_push`, or `no_reply` prevented the external action.
|
|
81
|
+
- `failed_action`: a reply or resolve action was attempted and failed; include the attempted action and failure in the final summary.
|
|
82
|
+
|
|
83
|
+
In normal mode, build and execute a platform action queue after fixes are verified and pushed when needed:
|
|
84
|
+
|
|
85
|
+
- `reply_then_resolve`: use for handled threads where the reviewer needs context before resolution.
|
|
86
|
+
- `resolve_only`: use for self-evident fixes and already-addressed or outdated threads where an extra reply would add noise.
|
|
87
|
+
- `reply_leave_open`: use only for clarification requests, blocked work, or intentionally open follow-ups.
|
|
88
|
+
- `reply_only`: use for PR-level comments or summaries that cannot be resolved as review threads.
|
|
89
|
+
|
|
90
|
+
For duplicate findings, execute the terminal action for every source thread ID, not only the primary triage record. If one finding is represented by three unresolved inline threads, all three must be resolved or explicitly left open.
|
|
91
|
+
|
|
92
|
+
## GitHub Action Guidance
|
|
93
|
+
|
|
94
|
+
Prefer platform-native APIs or `gh` commands that expose review-thread resolution state. For GitHub inline review threads, use the thread node ID and the GraphQL `resolveReviewThread` mutation rather than assuming that a reply resolves the conversation.
|
|
95
|
+
|
|
96
|
+
A reliable pattern is:
|
|
97
|
+
|
|
98
|
+
1. Re-fetch review threads and comments immediately before acting.
|
|
99
|
+
2. Reply to the thread when the action queue says a reply is needed.
|
|
100
|
+
3. Resolve the review thread by node ID when the terminal state should be `resolved`.
|
|
101
|
+
4. Re-fetch unresolved review threads after the action queue completes.
|
|
102
|
+
5. Retry any expected-to-be-resolved thread that is still unresolved once; if it still remains unresolved, mark it `failed_action` instead of claiming completion.
|
|
103
|
+
|
|
104
|
+
Example GraphQL mutation shape:
|
|
105
|
+
|
|
106
|
+
```graphql
|
|
107
|
+
mutation($threadId: ID!) {
|
|
108
|
+
resolveReviewThread(input: {threadId: $threadId}) {
|
|
109
|
+
thread {
|
|
110
|
+
id
|
|
111
|
+
isResolved
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
A posted reply alone is sufficient only for `reply_leave_open`, `reply_only`, or `not_resolvable` sources. For handled inline review threads, reply and resolve are separate actions.
|
|
118
|
+
|
|
73
119
|
## Flow
|
|
74
120
|
|
|
75
121
|
```mermaid
|
|
@@ -80,7 +126,7 @@ flowchart TD
|
|
|
80
126
|
D --> E{Classify each triage record}
|
|
81
127
|
E -->|Fix| F[Implement minimal change]
|
|
82
128
|
E -->|Answer| G[Prepare concise reply]
|
|
83
|
-
E -->|Clarify| H[
|
|
129
|
+
E -->|Clarify| H[Prepare question and leave open]
|
|
84
130
|
E -->|Already addressed or Outdated| I[Prepare evidence]
|
|
85
131
|
E -->|Defer or Won't fix| J[Document reason]
|
|
86
132
|
F --> K[Verify]
|
|
@@ -92,11 +138,13 @@ flowchart TD
|
|
|
92
138
|
L -->|dry_run| M[Report triage only]
|
|
93
139
|
L -->|no_push| N[Report local diff or commits]
|
|
94
140
|
L -->|no_reply| O[Report suggested replies/actions]
|
|
95
|
-
L -->|normal| P[Commit/push if changed
|
|
141
|
+
L -->|normal| P[Commit/push if changed]
|
|
142
|
+
P --> R[Execute reply/resolve action queue]
|
|
143
|
+
R --> S[Re-fetch threads and retry unresolved handled threads once]
|
|
96
144
|
M --> Q[Final summary]
|
|
97
145
|
N --> Q
|
|
98
146
|
O --> Q
|
|
99
|
-
|
|
147
|
+
S --> Q
|
|
100
148
|
```
|
|
101
149
|
|
|
102
150
|
## Compact Workflow
|
|
@@ -109,7 +157,7 @@ flowchart TD
|
|
|
109
157
|
2. **Classify each triage record**
|
|
110
158
|
- **Fix**: Valid requested change; make the smallest focused edit when not in `dry_run`.
|
|
111
159
|
- **Answer**: No code change needed; prepare a concise explanation.
|
|
112
|
-
- **Clarify**: Ambiguous, conflicting, or missing context;
|
|
160
|
+
- **Clarify**: Ambiguous, conflicting, or missing context; reply with the question and leave unresolved.
|
|
113
161
|
- **Already addressed**: Current code already satisfies it; prepare evidence.
|
|
114
162
|
- **Outdated**: Commented code or issue no longer exists; prepare evidence.
|
|
115
163
|
- **Defer / Won't fix**: Valid concern intentionally not changed now; document a specific reason.
|
|
@@ -118,17 +166,19 @@ flowchart TD
|
|
|
118
166
|
- Keep edits scoped to the review feedback.
|
|
119
167
|
- 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
168
|
- 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
|
|
169
|
+
- In `no_push`, local edits are allowed, but do not push or resolve threads whose fix is only local. Reply or resolve non-code, already-addressed, or outdated threads only when the action does not depend on unpushed work and `no_reply` is not set.
|
|
122
170
|
- In `no_reply`, do not post replies or resolve threads; report suggested replies/actions instead.
|
|
123
|
-
- In normal mode,
|
|
171
|
+
- In normal mode, commit and push changed code when appropriate, then execute the platform action queue for every collected source ID.
|
|
124
172
|
|
|
125
173
|
4. **Verify before claiming completion**
|
|
126
174
|
- For fixes, run appropriate checks or explain why they could not run.
|
|
127
175
|
- Re-inspect the updated diff and comment context to confirm the concern is resolved.
|
|
176
|
+
- Re-fetch review threads after reply/resolve actions and confirm all expected-to-be-resolved thread IDs are resolved.
|
|
128
177
|
- Do not mark a thread resolved if it still needs reviewer, maintainer, or product input.
|
|
178
|
+
- If a resolve or reply operation fails, retry once when safe; then report `failed_action` with the affected source ID and reason.
|
|
129
179
|
|
|
130
180
|
5. **Finish**
|
|
131
|
-
- Normal mode: commit/push changes when appropriate, post useful replies or a summary,
|
|
181
|
+
- Normal mode: commit/push changes when appropriate, post useful replies or a summary, resolve all handled threads by default, and reconcile the final unresolved set.
|
|
132
182
|
- Safe modes: report the local state and the exact replies/resolution actions a human could take.
|
|
133
183
|
|
|
134
184
|
## Reply Guidance
|
|
@@ -143,7 +193,9 @@ flowchart TD
|
|
|
143
193
|
|
|
144
194
|
- Mode used: `normal`, `dry_run`, `no_push`, or `no_reply`
|
|
145
195
|
- Counts by disposition: fixed, answered, clarified/left open, already addressed, outdated, deferred/won't-fix
|
|
146
|
-
-
|
|
196
|
+
- Counts by platform terminal state: resolved, replied-left-open, not-resolvable, skipped-by-mode, failed-action
|
|
197
|
+
- Threads resolved, intentionally left open, already resolved, or resolution actions skipped by mode
|
|
198
|
+
- Any expected-to-be-resolved thread that remained unresolved after retry
|
|
147
199
|
- Verification run or planned
|
|
148
200
|
- Commits pushed, local diff/commits, or "none"
|
|
149
201
|
- Remaining open items and who needs to respond
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mt5cli
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.3
|
|
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>
|
|
@@ -273,7 +273,7 @@ eurusd_m1 = rates["EURUSD", "M1"] # closed bars only
|
|
|
273
273
|
```
|
|
274
274
|
|
|
275
275
|
- **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.
|
|
276
|
-
- **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).
|
|
276
|
+
- **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). Pass `update_backend` to inject a custom history update callable (same keyword arguments as `update_history`) instead of monkey-patching `mt5cli.sdk.update_history`.
|
|
277
277
|
- **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.
|
|
278
278
|
- **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.
|
|
279
279
|
- **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.
|
|
@@ -249,7 +249,7 @@ eurusd_m1 = rates["EURUSD", "M1"] # closed bars only
|
|
|
249
249
|
```
|
|
250
250
|
|
|
251
251
|
- **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.
|
|
252
|
-
- **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).
|
|
252
|
+
- **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). Pass `update_backend` to inject a custom history update callable (same keyword arguments as `update_history`) instead of monkey-patching `mt5cli.sdk.update_history`.
|
|
253
253
|
- **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.
|
|
254
254
|
- **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.
|
|
255
255
|
- **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.
|
|
@@ -60,6 +60,7 @@ timestamp normalization in downstream apps.
|
|
|
60
60
|
| ------------------------------------------------ | ------------------------------------------------------------ |
|
|
61
61
|
| `drop_forming_rate_bar` | Remove the last row from chronologically ordered rate data |
|
|
62
62
|
| `fetch_latest_closed_rates` | Single connected client: fetch `count + 1`, drop forming bar |
|
|
63
|
+
| `fetch_latest_closed_rates_for_trading_client` | Closed bars from an active `Mt5TradingClient` session |
|
|
63
64
|
| `collect_latest_closed_rates_for_accounts` | Multi-account closed bars with optional retry wrapper |
|
|
64
65
|
| `collect_latest_closed_rates_by_granularity` | Same data keyed by `(symbol, granularity_name)` |
|
|
65
66
|
| `collect_latest_rates_for_accounts` | Latest bars including the forming bar when `start_pos=0` |
|
|
@@ -67,16 +68,16 @@ timestamp normalization in downstream apps.
|
|
|
67
68
|
|
|
68
69
|
### SQLite history collection and rate loading
|
|
69
70
|
|
|
70
|
-
| Symbol | Role
|
|
71
|
-
| ----------------------------------------------------------------------------------------------------------------------------- |
|
|
72
|
-
| `collect_history` | One-shot date-range export into SQLite
|
|
73
|
-
| `update_history`, `update_history_with_config` | Incremental append from `MAX(time)` cursors
|
|
74
|
-
| `ThrottledHistoryUpdater` | Minimum interval between successful incremental updates
|
|
75
|
-
| `resolve_history_datasets`, `resolve_history_timeframes`, `resolve_history_tick_flags` | History pipeline configuration
|
|
76
|
-
| `build_rate_view_name`, `resolve_rate_table_name`, `resolve_rate_view_name`, `resolve_rate_view_names`, `resolve_rate_tables` | Map symbols/timeframes to mt5cli-managed table or view names
|
|
77
|
-
| `RateTarget`, `build_rate_targets` | Neutral `(symbol, timeframe)` series descriptors
|
|
78
|
-
| `load_rate_data`, `load_rate_data_from_connection` | Load one table/view into a time-indexed DataFrame
|
|
79
|
-
| `load_rate_series_from_sqlite`, `load_rate_series_by_granularity` | Load one or many series; fail clearly when managed views are missing
|
|
71
|
+
| Symbol | Role |
|
|
72
|
+
| ----------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
73
|
+
| `collect_history` | One-shot date-range export into SQLite |
|
|
74
|
+
| `update_history`, `update_history_with_config` | Incremental append from `MAX(time)` cursors |
|
|
75
|
+
| `ThrottledHistoryUpdater` | Minimum interval between successful incremental updates; optional `update_backend` injection |
|
|
76
|
+
| `resolve_history_datasets`, `resolve_history_timeframes`, `resolve_history_tick_flags` | History pipeline configuration |
|
|
77
|
+
| `build_rate_view_name`, `resolve_rate_table_name`, `resolve_rate_view_name`, `resolve_rate_view_names`, `resolve_rate_tables` | Map symbols/timeframes to mt5cli-managed table or view names |
|
|
78
|
+
| `RateTarget`, `build_rate_targets` | Neutral `(symbol, timeframe)` series descriptors |
|
|
79
|
+
| `load_rate_data`, `load_rate_data_from_connection` | Load one table/view into a time-indexed DataFrame |
|
|
80
|
+
| `load_rate_series_from_sqlite`, `load_rate_series_by_granularity` | Load one or many series; fail clearly when managed views are missing |
|
|
80
81
|
|
|
81
82
|
Pass `require_existing=True` to rate view resolution helpers when downstream
|
|
82
83
|
code must fail instead of receiving a best-guess view name. Multi-series loaders
|
|
@@ -96,6 +97,7 @@ strategy entries, exits, Kelly sizing, or signal logic.
|
|
|
96
97
|
| `detect_position_side` | Net long / short / flat from open positions |
|
|
97
98
|
| `calculate_spread_ratio` | Relative bid-ask spread |
|
|
98
99
|
| `calculate_margin_and_volume`, `calculate_volume_by_margin`, `calculate_new_position_margin_ratio` | Margin budget and volume sizing |
|
|
100
|
+
| `normalize_order_volume`, `estimate_order_margin`, `calculate_positions_margin` | Broker volume normalization and margin totals |
|
|
99
101
|
| `determine_order_limits` | SL/TP price levels from ratios |
|
|
100
102
|
| `ensure_symbol_selected` | Select/verify Market Watch visibility |
|
|
101
103
|
| `place_market_order`, `close_open_positions`, `update_sltp_for_open_positions` | Order execution helpers (`dry_run` supported) |
|
|
@@ -31,9 +31,10 @@ 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. `fetch_latest_closed_rates()` handles one connected
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
row. `fetch_latest_closed_rates()` handles one connected `Mt5CliClient`; use
|
|
35
|
+
`fetch_latest_closed_rates_for_trading_client()` from an active
|
|
36
|
+
`Mt5TradingClient` session. Multi-account helpers fetch `count + 1` bars, drop
|
|
37
|
+
that row with `drop_forming_rate_bar()`, and validate each series is non-empty. Returned frames are ordered
|
|
37
38
|
oldest-to-newest and may contain fewer than `count` rows only when MT5 returns
|
|
38
39
|
fewer closed bars.
|
|
39
40
|
|
|
@@ -113,6 +114,28 @@ finally:
|
|
|
113
114
|
client.shutdown()
|
|
114
115
|
```
|
|
115
116
|
|
|
117
|
+
Pass `update_backend` to substitute the default `update_history` implementation
|
|
118
|
+
without monkey-patching `mt5cli.sdk.update_history`. The callable receives the
|
|
119
|
+
same keyword arguments as `update_history` (`client`, `output`, `symbols`,
|
|
120
|
+
`datasets`, `timeframes`, `flags`, `lookback_hours`, `with_views`,
|
|
121
|
+
`include_account_events`). The resolved backend is stored on
|
|
122
|
+
`updater.update_backend` for inspection or subclassing.
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from mt5cli import ThrottledHistoryUpdater, update_history
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def app_update_history(**kwargs) -> None:
|
|
129
|
+
update_history(**kwargs) # or delegate to application-specific logic
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
updater = ThrottledHistoryUpdater(
|
|
133
|
+
output="history.db",
|
|
134
|
+
interval_seconds=60,
|
|
135
|
+
update_backend=app_update_history,
|
|
136
|
+
)
|
|
137
|
+
```
|
|
138
|
+
|
|
116
139
|
By default recoverable errors (`Mt5TradingError`, `Mt5RuntimeError`,
|
|
117
140
|
`sqlite3.Error`, `ValueError`, `OSError`, and MT5 client capability
|
|
118
141
|
`AttributeError` / `TypeError` for history API methods) propagate so the caller
|
|
@@ -40,15 +40,19 @@ betting logic, or scheduling code in downstream applications.
|
|
|
40
40
|
|
|
41
41
|
```python
|
|
42
42
|
from mt5cli import (
|
|
43
|
+
calculate_positions_margin,
|
|
43
44
|
calculate_spread_ratio,
|
|
44
45
|
calculate_margin_and_volume,
|
|
45
46
|
close_open_positions,
|
|
46
47
|
detect_position_side,
|
|
47
48
|
determine_order_limits,
|
|
49
|
+
estimate_order_margin,
|
|
50
|
+
fetch_latest_closed_rates_for_trading_client,
|
|
48
51
|
get_account_snapshot,
|
|
49
52
|
get_positions_frame,
|
|
50
53
|
get_symbol_snapshot,
|
|
51
54
|
get_tick_snapshot,
|
|
55
|
+
normalize_order_volume,
|
|
52
56
|
place_market_order,
|
|
53
57
|
)
|
|
54
58
|
|
|
@@ -58,6 +62,22 @@ tick = get_tick_snapshot(client, "EURUSD")
|
|
|
58
62
|
positions = get_positions_frame(client, "EURUSD")
|
|
59
63
|
side = detect_position_side(client, "EURUSD")
|
|
60
64
|
spread_ratio = calculate_spread_ratio(client, "EURUSD")
|
|
65
|
+
volume = normalize_order_volume(
|
|
66
|
+
0.15,
|
|
67
|
+
volume_min=symbol["volume_min"],
|
|
68
|
+
volume_max=symbol["volume_max"],
|
|
69
|
+
volume_step=symbol["volume_step"],
|
|
70
|
+
)
|
|
71
|
+
buy_margin = (
|
|
72
|
+
estimate_order_margin(client, "EURUSD", "BUY", volume) if volume > 0 else 0.0
|
|
73
|
+
)
|
|
74
|
+
open_margin = calculate_positions_margin(client, symbols=["EURUSD"])
|
|
75
|
+
closed_bars = fetch_latest_closed_rates_for_trading_client(
|
|
76
|
+
client,
|
|
77
|
+
symbol="EURUSD",
|
|
78
|
+
granularity="M1",
|
|
79
|
+
count=100,
|
|
80
|
+
)
|
|
61
81
|
sizing = calculate_margin_and_volume(
|
|
62
82
|
client,
|
|
63
83
|
"EURUSD",
|
|
@@ -87,6 +107,12 @@ closed = close_open_positions(client, symbols="EURUSD", dry_run=True)
|
|
|
87
107
|
sell-only exposure, and `None` for no positions or mixed long/short exposure.
|
|
88
108
|
`calculate_spread_ratio()` uses `(ask - bid) / ((ask + bid) / 2)` and raises
|
|
89
109
|
`Mt5TradingError` when bid or ask is missing or non-positive.
|
|
110
|
+
`normalize_order_volume()` returns `0.0` for invalid constraints or
|
|
111
|
+
sub-minimum requests; check the result before calling `estimate_order_margin()`,
|
|
112
|
+
which requires a positive finite volume. `calculate_positions_margin()` silently
|
|
113
|
+
skips rows with missing symbols, non-positive volumes, non-finite volumes, or
|
|
114
|
+
unsupported position types, but propagates `Mt5TradingError` from `estimate_order_margin()` when a valid row
|
|
115
|
+
encounters invalid tick data or margin results from the broker.
|
|
90
116
|
|
|
91
117
|
SL/TP ratios for `determine_order_limits()` must satisfy `0 <= ratio < 1`; `0`
|
|
92
118
|
omits that level. SL/TP prices are rounded with symbol `digits` metadata when
|
|
@@ -153,6 +179,9 @@ through the stable package root without embedding entry/exit policy.
|
|
|
153
179
|
| Manual terminal spawn/kill around trading code | `mt5_trading_session()` |
|
|
154
180
|
| Local position-side detection | `detect_position_side()` |
|
|
155
181
|
| Local margin/volume sizing | `calculate_margin_and_volume()` |
|
|
182
|
+
| Local broker volume step normalization | `normalize_order_volume()` |
|
|
183
|
+
| Local order or position margin estimation | `estimate_order_margin()`, `calculate_positions_margin()` |
|
|
184
|
+
| Local closed-bar fetch from a trading session | `fetch_latest_closed_rates_for_trading_client()` |
|
|
156
185
|
| Local SL/TP price derivation | `determine_order_limits()` |
|
|
157
186
|
| Throttled SQLite history loop with ad-hoc error handling | `ThrottledHistoryUpdater(suppress_errors=True)` |
|
|
158
187
|
|
|
@@ -118,6 +118,7 @@ from .trading import (
|
|
|
118
118
|
PositionSide,
|
|
119
119
|
calculate_margin_and_volume,
|
|
120
120
|
calculate_new_position_margin_ratio,
|
|
121
|
+
calculate_positions_margin,
|
|
121
122
|
calculate_spread_ratio,
|
|
122
123
|
calculate_volume_by_margin,
|
|
123
124
|
close_open_positions,
|
|
@@ -125,11 +126,14 @@ from .trading import (
|
|
|
125
126
|
detect_position_side,
|
|
126
127
|
determine_order_limits,
|
|
127
128
|
ensure_symbol_selected,
|
|
129
|
+
estimate_order_margin,
|
|
130
|
+
fetch_latest_closed_rates_for_trading_client,
|
|
128
131
|
get_account_snapshot,
|
|
129
132
|
get_positions_frame,
|
|
130
133
|
get_symbol_snapshot,
|
|
131
134
|
get_tick_snapshot,
|
|
132
135
|
mt5_trading_session,
|
|
136
|
+
normalize_order_volume,
|
|
133
137
|
place_market_order,
|
|
134
138
|
update_sltp_for_open_positions,
|
|
135
139
|
)
|
|
@@ -182,6 +186,7 @@ __all__ = [
|
|
|
182
186
|
"build_rate_view_name",
|
|
183
187
|
"calculate_margin_and_volume",
|
|
184
188
|
"calculate_new_position_margin_ratio",
|
|
189
|
+
"calculate_positions_margin",
|
|
185
190
|
"calculate_spread_ratio",
|
|
186
191
|
"calculate_volume_by_margin",
|
|
187
192
|
"call_with_normalized_errors",
|
|
@@ -204,9 +209,11 @@ __all__ = [
|
|
|
204
209
|
"drop_forming_rate_bar",
|
|
205
210
|
"ensure_symbol_selected",
|
|
206
211
|
"ensure_utc",
|
|
212
|
+
"estimate_order_margin",
|
|
207
213
|
"export_dataframe",
|
|
208
214
|
"export_dataframe_to_sqlite",
|
|
209
215
|
"fetch_latest_closed_rates",
|
|
216
|
+
"fetch_latest_closed_rates_for_trading_client",
|
|
210
217
|
"get_account_snapshot",
|
|
211
218
|
"get_positions_frame",
|
|
212
219
|
"get_symbol_snapshot",
|
|
@@ -230,6 +237,7 @@ __all__ = [
|
|
|
230
237
|
"mt5_version",
|
|
231
238
|
"normalize_dataframe",
|
|
232
239
|
"normalize_mt5_exception",
|
|
240
|
+
"normalize_order_volume",
|
|
233
241
|
"normalize_symbol",
|
|
234
242
|
"normalize_symbols",
|
|
235
243
|
"normalize_time_columns",
|
|
@@ -30,6 +30,7 @@ STABLE_SDK_EXPORTS: frozenset[str] = frozenset({
|
|
|
30
30
|
"build_rate_view_name",
|
|
31
31
|
"calculate_margin_and_volume",
|
|
32
32
|
"calculate_new_position_margin_ratio",
|
|
33
|
+
"calculate_positions_margin",
|
|
33
34
|
"calculate_spread_ratio",
|
|
34
35
|
"calculate_volume_by_margin",
|
|
35
36
|
"call_with_normalized_errors",
|
|
@@ -50,9 +51,11 @@ STABLE_SDK_EXPORTS: frozenset[str] = frozenset({
|
|
|
50
51
|
"determine_order_limits",
|
|
51
52
|
"drop_forming_rate_bar",
|
|
52
53
|
"ensure_symbol_selected",
|
|
54
|
+
"estimate_order_margin",
|
|
53
55
|
"export_dataframe",
|
|
54
56
|
"export_dataframe_to_sqlite",
|
|
55
57
|
"fetch_latest_closed_rates",
|
|
58
|
+
"fetch_latest_closed_rates_for_trading_client",
|
|
56
59
|
"get_account_snapshot",
|
|
57
60
|
"get_positions_frame",
|
|
58
61
|
"get_symbol_snapshot",
|
|
@@ -74,6 +77,7 @@ STABLE_SDK_EXPORTS: frozenset[str] = frozenset({
|
|
|
74
77
|
"mt5_trading_session",
|
|
75
78
|
"mt5_version",
|
|
76
79
|
"normalize_mt5_exception",
|
|
80
|
+
"normalize_order_volume",
|
|
77
81
|
"orders",
|
|
78
82
|
"place_market_order",
|
|
79
83
|
"positions",
|
|
@@ -42,6 +42,8 @@ from .utils import coerce_login as _coerce_login
|
|
|
42
42
|
if TYPE_CHECKING:
|
|
43
43
|
from collections.abc import Callable, Iterator, Sequence
|
|
44
44
|
|
|
45
|
+
UpdateHistoryBackend = Callable[..., None]
|
|
46
|
+
|
|
45
47
|
T = TypeVar("T")
|
|
46
48
|
|
|
47
49
|
logger = logging.getLogger(__name__)
|
|
@@ -1044,10 +1046,16 @@ def update_history_with_config( # noqa: PLR0913
|
|
|
1044
1046
|
class ThrottledHistoryUpdater:
|
|
1045
1047
|
"""Throttled incremental SQLite history updater for long-running apps.
|
|
1046
1048
|
|
|
1047
|
-
Wraps :func:`update_history`
|
|
1048
|
-
updates, so a tight application loop can call
|
|
1049
|
-
iteration without re-fetching MT5 history more often
|
|
1050
|
-
uses a monotonic clock, so it is unaffected by
|
|
1049
|
+
Wraps :func:`update_history` (or a custom ``update_backend``) with a minimum
|
|
1050
|
+
interval between successful updates, so a tight application loop can call
|
|
1051
|
+
:meth:`update` every iteration without re-fetching MT5 history more often
|
|
1052
|
+
than desired. Timing uses a monotonic clock, so it is unaffected by
|
|
1053
|
+
wall-clock changes.
|
|
1054
|
+
|
|
1055
|
+
Downstream applications may pass ``update_backend`` to substitute the
|
|
1056
|
+
default :func:`update_history` implementation—for example to add
|
|
1057
|
+
application-specific logging, metrics, or test doubles—without monkey-
|
|
1058
|
+
patching ``mt5cli.sdk.update_history``.
|
|
1051
1059
|
"""
|
|
1052
1060
|
|
|
1053
1061
|
def __init__(
|
|
@@ -1062,6 +1070,7 @@ class ThrottledHistoryUpdater:
|
|
|
1062
1070
|
include_account_events: bool = True,
|
|
1063
1071
|
interval_seconds: float = 0.0,
|
|
1064
1072
|
suppress_errors: bool = False,
|
|
1073
|
+
update_backend: UpdateHistoryBackend | None = None,
|
|
1065
1074
|
) -> None:
|
|
1066
1075
|
"""Initialize the throttled updater.
|
|
1067
1076
|
|
|
@@ -1085,6 +1094,12 @@ class ThrottledHistoryUpdater:
|
|
|
1085
1094
|
the throttle. Other ``AttributeError`` / ``TypeError`` values
|
|
1086
1095
|
always propagate. When False (default), recoverable errors
|
|
1087
1096
|
propagate so callers control logging.
|
|
1097
|
+
update_backend: Callable invoked instead of :func:`update_history`
|
|
1098
|
+
when :meth:`update` runs. Receives the same keyword arguments as
|
|
1099
|
+
:func:`update_history` (``client``, ``output``, ``symbols``,
|
|
1100
|
+
``datasets``, ``timeframes``, ``flags``, ``lookback_hours``,
|
|
1101
|
+
``with_views``, ``include_account_events``). Defaults to
|
|
1102
|
+
:func:`update_history`.
|
|
1088
1103
|
"""
|
|
1089
1104
|
self.output = output
|
|
1090
1105
|
self.datasets = datasets
|
|
@@ -1095,6 +1110,9 @@ class ThrottledHistoryUpdater:
|
|
|
1095
1110
|
self.include_account_events = include_account_events
|
|
1096
1111
|
self.interval_seconds = interval_seconds
|
|
1097
1112
|
self.suppress_errors = suppress_errors
|
|
1113
|
+
self.update_backend = (
|
|
1114
|
+
update_history if update_backend is None else update_backend
|
|
1115
|
+
)
|
|
1098
1116
|
self._last_update_monotonic: float | None = None
|
|
1099
1117
|
|
|
1100
1118
|
@property
|
|
@@ -1145,7 +1163,7 @@ class ThrottledHistoryUpdater:
|
|
|
1145
1163
|
lookback_hours=self.lookback_hours,
|
|
1146
1164
|
date_to=None,
|
|
1147
1165
|
)
|
|
1148
|
-
|
|
1166
|
+
self.update_backend(
|
|
1149
1167
|
client=client,
|
|
1150
1168
|
output=self.output,
|
|
1151
1169
|
symbols=symbols,
|