5b1d54bfe9
* Add SDK orchestration helpers for resilient multi-account collection
- collect_latest_rates_for_accounts_with_retries(): exponential-backoff
retries around collect_latest_rates_for_accounts(), retrying only
Mt5TradingError/Mt5RuntimeError and re-raising on exhaustion.
- resolve_account_spec()/resolve_account_specs() and
substitute_env_placeholders(): merge explicit overrides over AccountSpec
fields and expand ${ENV_VAR} placeholders, raising ValueError on missing
variables.
- ThrottledHistoryUpdater: monotonic-clock throttled wrapper around
update_history() with should_update()/update() and opt-in suppress_errors.
- load_rate_series_by_granularity(): rate-series loader keyed by
(symbol | None, granularity_name).
- Export new APIs, add unit tests (100% coverage), and document in README
and docs/api.
* chore: bump version from 0.5.1 to 0.5.3 (#24)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
* fix: resolve leftover merge conflict markers in version files
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
* fix: address PR review feedback on SDK orchestration helpers
- Use single-pass env substitution to avoid TOCTOU KeyError
- Apply backoff_base to all retry delays (backoff_base ** (attempt + 1))
- Preserve integer logins in resolve_account_spec; hide login in repr
- Fix docs examples (env ordering, while True loop, backoff comment)
- Parametrize suppress_errors tests for MT5 and SQLite errors
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
84 lines
2.5 KiB
Markdown
84 lines
2.5 KiB
Markdown
# SDK Module
|
|
|
|
::: mt5cli.sdk
|
|
|
|
## Resilient multi-account orchestration
|
|
|
|
The SDK ships strategy-agnostic helpers for building long-running collectors on
|
|
top of the read-only client. None of them depend on a particular trading
|
|
application.
|
|
|
|
### Retrying transient rate collection
|
|
|
|
`collect_latest_rates_for_accounts_with_retries()` wraps
|
|
`collect_latest_rates_for_accounts()` with bounded exponential backoff. Only
|
|
`pdmt5.Mt5TradingError` and `pdmt5.Mt5RuntimeError` are retried; the final
|
|
failure is re-raised once `retry_count` is exhausted.
|
|
|
|
```python
|
|
from mt5cli import AccountSpec, collect_latest_rates_for_accounts_with_retries
|
|
|
|
accounts = [AccountSpec(symbols=["EURUSD"], login=12345)]
|
|
rates = collect_latest_rates_for_accounts_with_retries(
|
|
accounts,
|
|
["M1", "H1"],
|
|
count=500,
|
|
retry_count=3,
|
|
backoff_base=2, # sleeps 2s, 4s, 8s between attempts
|
|
)
|
|
```
|
|
|
|
### Resolving credentials and `${ENV_VAR}` placeholders
|
|
|
|
`resolve_account_spec()` / `resolve_account_specs()` merge explicit override
|
|
values over `AccountSpec` fields and expand `${ENV_VAR}` placeholders, keeping
|
|
secrets out of plan/config files. A missing environment variable raises
|
|
`ValueError`.
|
|
|
|
```python
|
|
import os
|
|
|
|
from mt5cli import AccountSpec, resolve_account_specs
|
|
|
|
os.environ["MT5_LOGIN"] = "12345"
|
|
os.environ["MT5_PASSWORD"] = "secret"
|
|
accounts = [
|
|
AccountSpec(symbols=["EURUSD"], login="${MT5_LOGIN}", password="${MT5_PASSWORD}")
|
|
]
|
|
|
|
resolved = resolve_account_specs(accounts, server="Broker-Demo")
|
|
# resolved[0].login == "12345", resolved[0].server == "Broker-Demo"
|
|
```
|
|
|
|
### Throttled incremental history updates
|
|
|
|
`ThrottledHistoryUpdater` wraps `update_history()` with a minimum interval
|
|
between successful runs (using a monotonic clock), so an application loop can
|
|
call it every iteration without over-fetching.
|
|
|
|
```python
|
|
from pdmt5 import Mt5Config, Mt5DataClient
|
|
|
|
from mt5cli import Dataset, ThrottledHistoryUpdater
|
|
|
|
updater = ThrottledHistoryUpdater(
|
|
output="history.db",
|
|
datasets={Dataset.rates},
|
|
timeframes=["M1"],
|
|
interval_seconds=60, # <= 0 updates on every call
|
|
)
|
|
|
|
client = Mt5DataClient(config=Mt5Config(login=12345))
|
|
client.initialize_and_login_mt5()
|
|
try:
|
|
while True:
|
|
updater.update(client, ["EURUSD", "GBPUSD"]) # no-op until 60s elapse
|
|
# ... do other work; break when shutting down ...
|
|
finally:
|
|
client.shutdown()
|
|
```
|
|
|
|
By default `Mt5TradingError`, `Mt5RuntimeError`, and `sqlite3.Error` propagate so
|
|
the caller controls logging; pass `suppress_errors=True` to swallow them and
|
|
return `False` without advancing the throttle.
|