Files
mt5cli/docs/api/sdk.md
T
Daichi Narushima c2cf0656dd Drop Mt5TradingError support; require pdmt5>=1.0.4 (#101)
* chore: upgrade pdmt5 to v1.0.4

pdmt5 1.0.4 removes Mt5TradingClient/Mt5TradingError entirely and wraps
Mt5Config.password in pydantic SecretStr. Drop the now-dead conditional
Mt5TradingError handling in mt5cli.exceptions/sdk/retry (mt5cli already
type-checks trading clients against its own protocol, so no functional
change), and unwrap SecretStr when forwarding a base config's password to
per-account configs. Update tests and docs accordingly.

* chore: declare pydantic as a direct runtime dependency

mt5cli.sdk imports SecretStr directly from pydantic, so pin it explicitly
instead of relying on pdmt5's transitive dependency.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-04 01:30: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.Mt5RuntimeError is 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 trading session created by create_trading_client(). 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 ThrottledHistoryUpdater
from mt5cli.utils import Dataset

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.