Files
mt5cli/docs/api/sdk.md
T
Daichi Narushima 292fac899a Add generic trading helpers and reduce public API tiers (#58)
* feat: add generic trading helpers and API tiers

* Bump version to v0.9.3

* fix: require symbol digits for trailing stops

* fix: allow side-specific trailing stop ticks

* test: enforce complete public export tiers

* docs: align public contract tiers

* refactor: remove legacy public supports
2026-06-24 01:58:32 +09:00

5.8 KiB

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.

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
)

Latest closed rate bars

MetaTrader 5 start_pos=0 includes the still-forming current bar as the last row. fetch_latest_closed_rates() handles one connected MT5Client; use fetch_latest_closed_rates_for_trading_client() from an active Mt5TradingClient session. Multi-account helpers fetch count + 1 bars, drop that row with drop_forming_rate_bar(), and validate each series is non-empty. Returned frames are ordered oldest-to-newest and may contain fewer than count rows only when MT5 returns fewer closed bars.

from mt5cli import (
    AccountSpec,
    collect_latest_closed_rates_by_granularity,
    fetch_latest_closed_rates,
)

closed = fetch_latest_closed_rates(
    client,
    symbol="EURUSD",
    granularity="M1",
    count=500,
)

rates = collect_latest_closed_rates_by_granularity(
    [AccountSpec(symbols=["EURUSD"], login=12345)],
    ["M1", "H1"],
    count=500,
    retry_count=3,
)
closed_m1 = rates["EURUSD", "M1"]

Use collect_latest_closed_rates_by_granularity() when callers prefer keys such as ("EURUSD", "M1") instead of integer timeframes.

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.

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"

Pass allow_whole_dollar_env=True to also expand strings whose entire value is a bare $ENV_NAME identifier (no braces). This opt-in covers substitute_env_placeholders(), resolve_account_spec(), resolve_account_specs(), and build_config(). Note: build_config cannot expand login because that parameter is int | None; use resolve_account_spec for a string login placeholder. Partial strings such as "plan$pass", "abc$ENV", or "$ENV-suffix" are never expanded — only an exact $IDENTIFIER whole-string match qualifies. The default is False to preserve backward compatibility.

import os

from mt5cli import AccountSpec, resolve_account_specs

os.environ["MT5_PASSWORD"] = "secret"
accounts = [AccountSpec(symbols=["EURUSD"], password="$MT5_PASSWORD")]

resolved = resolve_account_specs(accounts, allow_whole_dollar_env=True)
# resolved[0].password == "secret"

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.

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()

Pass update_backend to substitute the default update_history implementation without monkey-patching mt5cli.sdk.update_history. The callable receives the same keyword arguments as update_history (client, output, symbols, datasets, timeframes, flags, lookback_hours, with_views, include_account_events). The resolved backend is stored on updater.update_backend for inspection or subclassing.

from mt5cli import ThrottledHistoryUpdater, update_history


def app_update_history(**kwargs) -> None:
    update_history(**kwargs)  # or delegate to application-specific logic


updater = ThrottledHistoryUpdater(
    output="history.db",
    interval_seconds=60,
    update_backend=app_update_history,
)

By default recoverable errors (Mt5TradingError, Mt5RuntimeError, sqlite3.Error, ValueError, OSError, and MT5 client capability AttributeError / TypeError for history API methods) propagate so the caller controls logging; pass suppress_errors=True to swallow them and return False without advancing the throttle. Other AttributeError / TypeError values always propagate. Input validation (_resolve_update_history_request) runs before any MT5 or SQLite calls, but when suppress_errors=True the resulting ValueError is suppressed along with other recoverable errors.

Trading-capable sessions

For order placement and trading calculations, use the dedicated Trading module. Use mt5_session() / MT5Client for read-only collection.