mt5cli 1.3.2__tar.gz → 1.4.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.
- {mt5cli-1.3.2 → mt5cli-1.4.0}/PKG-INFO +1 -1
- {mt5cli-1.3.2 → mt5cli-1.4.0}/docs/api/public-contract.md +1 -1
- {mt5cli-1.3.2 → mt5cli-1.4.0}/docs/api/trading.md +111 -45
- {mt5cli-1.3.2 → mt5cli-1.4.0}/mt5cli/trading.py +642 -272
- {mt5cli-1.3.2 → mt5cli-1.4.0}/pyproject.toml +1 -1
- {mt5cli-1.3.2 → mt5cli-1.4.0}/tests/test_trading.py +1659 -723
- {mt5cli-1.3.2 → mt5cli-1.4.0}/uv.lock +2 -2
- {mt5cli-1.3.2 → mt5cli-1.4.0}/.agents/skills/local-qa/SKILL.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/.agents/skills/local-qa/scripts/qa.sh +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/.agents/skills/mt5cli/SKILL.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/.agents/skills/pr-feedback-triage/SKILL.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/.claude/agents/codex.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/.claude/settings.json +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/.github/FUNDING.yml +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/.github/dependabot.yml +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/.github/renovate.json +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/.github/workflows/ci.yml +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/.github/workflows/claude.yml +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/.github/workflows/release.yml +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/.gitignore +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/AGENTS.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/CLAUDE.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/LICENSE +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/README.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/docs/api/cli.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/docs/api/client.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/docs/api/converters.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/docs/api/exceptions.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/docs/api/grafana.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/docs/api/history.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/docs/api/index.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/docs/api/marketdata.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/docs/api/observability.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/docs/api/schemas.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/docs/api/telemetry.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/docs/api/utils.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/docs/index.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/examples/grafana/README.md +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/examples/grafana/compose.yml +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/examples/grafana/dashboards/mt5cli-market.json +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/examples/grafana/dashboards/mt5cli-overview.json +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/examples/grafana/dashboards/mt5cli-trades.json +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/examples/grafana/provisioning/dashboards/mt5cli.yml +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/examples/grafana/provisioning/datasources/mt5cli-sqlite.yml +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/mkdocs.yml +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/mt5cli/__init__.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/mt5cli/__main__.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/mt5cli/cli.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/mt5cli/client.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/mt5cli/contract.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/mt5cli/converters.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/mt5cli/exceptions.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/mt5cli/grafana.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/mt5cli/history.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/mt5cli/marketdata.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/mt5cli/observability.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/mt5cli/retry.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/mt5cli/schemas.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/mt5cli/telemetry.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/mt5cli/utils.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/tests/__init__.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/tests/conftest.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/tests/test_cli.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/tests/test_client.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/tests/test_contracts.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/tests/test_examples.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/tests/test_grafana.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/tests/test_history.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/tests/test_marketdata.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/tests/test_observability.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/tests/test_packaging.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/tests/test_telemetry.py +0 -0
- {mt5cli-1.3.2 → mt5cli-1.4.0}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mt5cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
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>
|
|
@@ -102,7 +102,7 @@ strategy entries, exits, Kelly sizing, or signal logic.
|
|
|
102
102
|
| ------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------- |
|
|
103
103
|
| `get_account_snapshot`, `get_symbol_snapshot`, `get_tick_snapshot`, `get_positions_frame` | Normalized account/symbol/tick/position views |
|
|
104
104
|
| `extract_tick_price` | Positive finite bid/ask extraction from tick mappings |
|
|
105
|
-
| `TickClockNormalizer` | Connection-scoped,
|
|
105
|
+
| `TickClockNormalizer` | Connection-scoped, host-clock-validated UTC tick normalization |
|
|
106
106
|
| `TickClockCalibration` | Server-clock calibration diagnostics (offset, evidence, status) |
|
|
107
107
|
| `NormalizedTickSnapshot` | Typed latest-tick contract with raw vs validated UTC timestamps |
|
|
108
108
|
| `CalibrationStatus`, `ClockStatus` | Literal status types for clock calibration and normalization |
|
|
@@ -204,16 +204,27 @@ provide entry-deal classification, Kelly sizing, or any betting-specific helpers
|
|
|
204
204
|
|
|
205
205
|
## Timestamp sources and broker clock offsets
|
|
206
206
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
207
|
+
MetaQuotes documents `copy_ticks_range()` and `copy_rates_range()` results,
|
|
208
|
+
and the date bounds they accept, as UTC, and does not document a different
|
|
209
|
+
timezone contract for `symbol_info_tick()`. In production on at least one
|
|
210
|
+
OANDA-style broker/terminal, however, `symbol_info_tick()` returned epochs
|
|
211
|
+
carrying the broker's own wall-clock label (UTC+3), not true UTC: treating
|
|
212
|
+
`copy_ticks_range()` as an independent UTC reference for the live tick clock
|
|
213
|
+
was the direct cause of a production calibration failure (see
|
|
214
|
+
[dceoy/mteor#428](https://github.com/dceoy/mteor/issues/428)), because a
|
|
215
|
+
query window built from the host clock missed the live event entirely, off
|
|
216
|
+
by exactly the broker's offset. mt5cli does not call `copy_ticks_range()` or
|
|
217
|
+
`copy_rates_range()` to calibrate the live tick clock, and does not
|
|
218
|
+
independently verify their documented UTC contract on any broker or
|
|
219
|
+
terminal — treat copied-history timestamps as broker/terminal-dependent
|
|
220
|
+
absent evidence from your own broker/terminal combination.
|
|
211
221
|
|
|
212
222
|
`get_tick_snapshot()` preserves the numeric MT5 epoch value in `time`; it does
|
|
213
|
-
not expose pdmt5's timezone-naive `Timestamp` conversion or alter the instant
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
223
|
+
not expose pdmt5's timezone-naive `Timestamp` conversion or alter the instant,
|
|
224
|
+
and it never applies any offset correction. Any broker-specific offset
|
|
225
|
+
handling requires independent evidence — see `TickClockNormalizer` below —
|
|
226
|
+
and must be applied separately. Do not subtract a presumed UTC+2 or UTC+3
|
|
227
|
+
offset from a raw timestamp without that evidence.
|
|
217
228
|
|
|
218
229
|
No offset is applied automatically by history retrieval helpers.
|
|
219
230
|
|
|
@@ -251,47 +262,60 @@ A normalized snapshot keeps the raw/UTC distinction explicit:
|
|
|
251
262
|
|
|
252
263
|
### Calibration evidence
|
|
253
264
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
`
|
|
263
|
-
|
|
265
|
+
`copy_ticks_range()` is not used for calibration: its documented UTC contract
|
|
266
|
+
is broker/terminal-dependent and not independently verified here (see
|
|
267
|
+
above), so neither the query timestamps nor the returned historical tick
|
|
268
|
+
timestamps can serve as an out-of-band UTC reference, and historical copied
|
|
269
|
+
rows are never used to infer the server offset. Instead, the only
|
|
270
|
+
independent reference available is **this process's own clock**: every
|
|
271
|
+
calibration poll
|
|
272
|
+
fetches one live `symbol_info_tick()` value and stamps it with
|
|
273
|
+
`datetime.now(UTC)` at the moment it is received. Comparing that host receipt
|
|
274
|
+
time against the tick's own server-labeled epoch yields one candidate offset,
|
|
275
|
+
which must align to a 30-minute increment within the realistic UTC-14..UTC+14
|
|
276
|
+
range. `time_msc` is preferred over second-resolution `time` when available.
|
|
277
|
+
|
|
278
|
+
A single poll is never trusted on its own. A closed, stale, or illiquid
|
|
279
|
+
symbol keeps returning the exact same last-traded tick, so its epoch never
|
|
280
|
+
changes between polls — that ordinary tick age is not calibration evidence.
|
|
281
|
+
Only a poll whose epoch _differs_ from the previous poll (comparison is
|
|
282
|
+
inequality, not "must increase": a genuine broker offset **decrease**, e.g. a
|
|
283
|
+
UTC+3 to UTC+2 DST fallback, relabels the next fresh event with a numerically
|
|
284
|
+
_smaller_ epoch than the last one observed under the old, larger offset)
|
|
285
|
+
proves the broker produced a new event, and only then is its offset computed.
|
|
264
286
|
|
|
265
287
|
An offset is accepted only after `min_agreeing_samples` (default 2)
|
|
266
|
-
**distinct**
|
|
267
|
-
|
|
288
|
+
**distinct** fresh tick events agree on the same rounded offset — repeated
|
|
289
|
+
polls of one unchanged tick never count as evidence, so an illiquid symbol
|
|
268
290
|
cannot self-confirm. Evidence can come from multiple samples of one actively
|
|
269
291
|
updating symbol (paced by `sample_interval_seconds`) or across several
|
|
270
|
-
symbols passed to the constructor
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
`_MAX_FUTURE_SKEW_SECONDS` (2 minutes) past the host clock's current time, in
|
|
275
|
-
addition to covering the full trailing `copied_window_seconds`. A live tick's
|
|
276
|
-
true UTC event is not always at or before the host clock — feed/broker clock
|
|
277
|
-
skew can put it a few seconds ahead — and without that tolerance the matching
|
|
278
|
-
copied row would never be queried, so calibration would always report
|
|
279
|
-
`no_matching_event`. The tolerance is bounded, not open-ended: an event
|
|
280
|
-
further ahead than `_MAX_FUTURE_SKEW_SECONDS` is still excluded from the
|
|
281
|
-
query and cannot be matched.
|
|
292
|
+
symbols passed to the constructor; a closed symbol among several configured
|
|
293
|
+
symbols simply contributes no evidence rather than blocking the others. Any
|
|
294
|
+
two fresh events that disagree on the rounded offset abort the calibration
|
|
295
|
+
(`offset_disagreement`) instead of being averaged or resolved by recency.
|
|
282
296
|
|
|
283
297
|
### Failure modes and fail-closed behavior
|
|
284
298
|
|
|
285
|
-
When calibration cannot be established safely —
|
|
286
|
-
(
|
|
287
|
-
(`
|
|
288
|
-
|
|
289
|
-
(`implausible_offset`)
|
|
299
|
+
When calibration cannot be established safely — no symbol ever produced a
|
|
300
|
+
fresh (changed-epoch) tick, e.g. every configured symbol is closed or
|
|
301
|
+
illiquid (`not_advancing`), a fresh event's raw offset does not round cleanly
|
|
302
|
+
to a plausible bucket (`unstable_offset`), an unrealistic rounded delta
|
|
303
|
+
(`implausible_offset`), too few distinct agreeing events
|
|
304
|
+
(`insufficient_agreement`), disagreeing events (`offset_disagreement`), or
|
|
305
|
+
missing live data (`no_live_tick`) — snapshots fail closed: `clock_status` is
|
|
290
306
|
`"uncalibrated"`, `time_utc` and `server_clock_offset_seconds` are `None`,
|
|
291
307
|
and the raw fields remain available. A raw timestamp is never silently
|
|
292
|
-
treated as UTC.
|
|
308
|
+
treated as UTC. The same fail-closed rule applies after a successful
|
|
309
|
+
calibration: once periodic revalidation invalidates the cached offset (see
|
|
310
|
+
below), subsequent snapshots are normalized only by a successful full
|
|
311
|
+
recalibration — never by the refuted cache. `normalizer.calibration`
|
|
312
|
+
exposes the full
|
|
293
313
|
`TickClockCalibration` diagnostics: status, offset, distinct sample count,
|
|
294
|
-
evidence symbols,
|
|
314
|
+
evidence symbols, last calibration time, and (for troubleshooting a failed
|
|
315
|
+
attempt) the most recently considered sample's symbol, raw server-labeled
|
|
316
|
+
time, host observation time, and raw (pre-rounding) offset —
|
|
317
|
+
`to_dict()` for serialization, and all of it is included in the one
|
|
318
|
+
concise warning logged per failed calibration attempt.
|
|
295
319
|
|
|
296
320
|
### Caching and revalidation
|
|
297
321
|
|
|
@@ -305,12 +329,54 @@ symbols and calls. A cached offset is recomputed in three cases:
|
|
|
305
329
|
future — evidence that the broker offset grew, e.g. a UTC+2 to UTC+3
|
|
306
330
|
transition.
|
|
307
331
|
- Periodically, at most once per `revalidation_interval_seconds` (default 5
|
|
308
|
-
minutes),
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
332
|
+
minutes), through one full revalidation round over every configured
|
|
333
|
+
symbol. This is what catches a broker offset _decrease_ (e.g. UTC+3 to
|
|
334
|
+
UTC+2): the resulting normalized time looks stale rather than future,
|
|
335
|
+
which a future-skew check alone cannot distinguish from ordinary
|
|
336
|
+
quiet-market staleness.
|
|
337
|
+
|
|
338
|
+
In a revalidation round each configured symbol is polled once and compared
|
|
339
|
+
against that symbol's last known observation (from the initial calibration
|
|
340
|
+
or a prior revalidation), and the whole round is evaluated before any
|
|
341
|
+
decision is made — revalidation never stops at the first confirming symbol.
|
|
342
|
+
A symbol is inconclusive — skipped, neither confirming nor refuting the
|
|
343
|
+
cache — when its fetch is unusable, when it has no prior observation to
|
|
344
|
+
compare against, or when its tick epoch is unchanged since that observation
|
|
345
|
+
(ordinary tick age, e.g. a closed or illiquid symbol). Every other advancing
|
|
346
|
+
sample is evidence the cache must be able to explain:
|
|
347
|
+
|
|
348
|
+
- an accepted offset equal to the cached one confirms the cache, but never
|
|
349
|
+
cancels another symbol's refuting evidence from the same round;
|
|
350
|
+
- an accepted offset that differs from the cached one refutes the cache (a
|
|
351
|
+
clean broker offset change) — unless the cached offset, widened by the
|
|
352
|
+
elapsed time since that symbol's prior observation plus that prior
|
|
353
|
+
observation's own inferred delay under the cached offset, still explains
|
|
354
|
+
the disagreeing raw offset. That case is forgiven once per symbol and held as
|
|
355
|
+
a pending disagreement instead of refuting immediately, since it cannot
|
|
356
|
+
yet be told apart from a maximally delayed tick under the cached offset.
|
|
357
|
+
A pending disagreement escalates to a refutation only if that same
|
|
358
|
+
symbol's next conclusive round rounds to the identical bucket again; any
|
|
359
|
+
inconclusive, differently-bucketed, or unusable round in between clears
|
|
360
|
+
it instead of letting unrelated samples accumulate into false
|
|
361
|
+
corroboration;
|
|
362
|
+
- an advancing sample with no usable offset (`unstable_offset` or
|
|
363
|
+
`implausible_offset`) also refutes the cache, subject to the same
|
|
364
|
+
once-per-symbol forgiveness above — but an unusable sample can never
|
|
365
|
+
itself be held as, or match, a pending disagreement, since it carries no
|
|
366
|
+
specific offset to corroborate. A fresh market event whose raw offset is
|
|
367
|
+
neither reconciled with any plausible 30-minute bucket nor forgiven this
|
|
368
|
+
way is contradictory evidence the cached calibration cannot explain.
|
|
369
|
+
|
|
370
|
+
Any refuting evidence invalidates the cached calibration and forces full
|
|
371
|
+
recalibration; when several symbols refute the cache in one round, the
|
|
372
|
+
reported evidence is deterministic (`implausible_offset` first, then
|
|
373
|
+
`unstable_offset`, then a clean disagreement). The recalibration either
|
|
374
|
+
re-establishes a validated offset or fails closed — a raw tick is never
|
|
375
|
+
normalized with a cached offset that advancing evidence has just refuted. A
|
|
376
|
+
wholly inconclusive round keeps the cache, and a round in which no symbol
|
|
377
|
+
had a comparable baseline does not consume the interval, so the next call
|
|
378
|
+
retries immediately instead of waiting a full
|
|
379
|
+
`revalidation_interval_seconds`.
|
|
314
380
|
|
|
315
381
|
If recalibration still cannot validate the timestamp, the snapshot fails
|
|
316
382
|
closed. Failed calibrations are recorded for diagnostics but never reused;
|