From 42f16af1aa53d3c0cd28eb9e4f6bc4b40f0f73f6 Mon Sep 17 00:00:00 2001 From: dceoy Date: Thu, 11 Jun 2026 14:23:26 +0000 Subject: [PATCH] deploy: 0fad55d609d4e1db98f90006c755606dde5abeae --- api/cli/index.html | 8 +- api/history/index.html | 114 +- api/sdk/index.html | 3200 +++++++++++++++++++------------------- api/utils/index.html | 750 +++++---- index.html | 12 +- objects.inv | Bin 2589 -> 2587 bytes search/search_index.json | 2 +- 7 files changed, 2038 insertions(+), 2048 deletions(-) diff --git a/api/cli/index.html b/api/cli/index.html index 63bbd75..283df78 100644 --- a/api/cli/index.html +++ b/api/cli/index.html @@ -303,7 +303,7 @@ (mt5cli.utils.TICK_FLAGS_TYPE)" href="../utils/#mt5cli.utils.TICK_FLAGS_TYPE">TICK_FLAGS_TYPE, help="Tick copy flags (ALL, INFO, TRADE, or integer).", ), - ] = 1, + ] = "ALL", if_exists: Annotated[ IfExists, Option( @@ -500,7 +500,7 @@ views cash_events and positions_reconstructed are deri click_type=TICK_FLAGS_TYPE, help="Tick copy flags (ALL, INFO, TRADE, or integer).", ), - ] = 1, + ] = "ALL", # pyright: ignore[reportArgumentType] if_exists: Annotated[ IfExists, typer.Option( @@ -2167,7 +2167,7 @@ views cash_events and positions_reconstructed are deri (mt5cli.utils.TICK_FLAGS_TYPE)" href="../utils/#mt5cli.utils.TICK_FLAGS_TYPE">TICK_FLAGS_TYPE, help="Tick flags (ALL, INFO, TRADE, or integer).", ), - ] = 1, + ] = "ALL", ) -> None @@ -2235,7 +2235,7 @@ views cash_events and positions_reconstructed are deri click_type=TICK_FLAGS_TYPE, help="Tick flags (ALL, INFO, TRADE, or integer).", ), - ] = 1, + ] = "ALL", # pyright: ignore[reportArgumentType] ) -> None: """Export ticks from a recent time window.""" client = _sdk_client(ctx) diff --git a/api/history/index.html b/api/history/index.html index b983a03..b459cd9 100644 --- a/api/history/index.html +++ b/api/history/index.html @@ -171,13 +171,13 @@ -
DEFAULT_HISTORY_TIMEFRAMES: tuple[str, ...] = tuple(
-    
DEFAULT_HISTORY_TIMEFRAMES: tuple[str, ...] = (
+    TIMEFRAME_MAP
+   (mt5cli.utils.TIMEFRAME_NAMES)" href="../utils/#mt5cli.utils.TIMEFRAME_NAMES">TIMEFRAME_NAMES
 )
 
@@ -3513,11 +3513,11 @@ fails.

Source code in mt5cli/history.py -
54
-55
-56
def quote_sqlite_identifier(identifier: str) -> str:
-    """Return a safely quoted SQLite identifier using double quotes."""
-    return '"' + identifier.replace('"', '""') + '"'
+              
55
+56
+57
def quote_sqlite_identifier(identifier: str) -> str:
+    """Return a safely quoted SQLite identifier using double quotes."""
+    return '"' + identifier.replace('"', '""') + '"'
 
@@ -3591,17 +3591,19 @@ fails.

Source code in mt5cli/history.py -
101
+              
def resolve_granularity_name(timeframe: int) -> str:
-    """Return a granularity name for a timeframe integer when known."""
-    for name, value in TIMEFRAME_MAP.items():
-        if value == timeframe:
-            return name
-    return str(timeframe)
+106
def resolve_granularity_name(timeframe: int) -> str:
+    """Return a granularity name for a timeframe integer when known."""
+    try:
+        name = _get_timeframe_name(timeframe)
+    except ValueError:
+        return str(timeframe)
+    return name.removeprefix("TIMEFRAME_")
 
@@ -3661,8 +3663,7 @@ fails.

Source code in mt5cli/history.py -
59
-60
+              
60
 61
 62
 63
@@ -3670,16 +3671,17 @@ fails.

65 66 67 -68
def resolve_history_datasets(datasets: set[Dataset] | None) -> set[Dataset]:
-    """Resolve configured history datasets.
-
-    Returns:
-        All supported datasets when ``datasets`` is None, otherwise the
-        configured selection (which may be empty).
-    """
-    if datasets is None:
-        return set(Dataset)
-    return set(datasets)
+68
+69
def resolve_history_datasets(datasets: set[Dataset] | None) -> set[Dataset]:
+    """Resolve configured history datasets.
+
+    Returns:
+        All supported datasets when ``datasets`` is None, otherwise the
+        configured selection (which may be empty).
+    """
+    if datasets is None:
+        return set(Dataset)
+    return set(datasets)
 
@@ -3727,23 +3729,19 @@ fails.

Source code in mt5cli/history.py -
90
-91
+              
def resolve_history_tick_flags(flags: int | str) -> int:
-    """Resolve tick copy flags from an integer or name.
-
-    Returns:
-        Integer tick flag value.
-    """
-    if isinstance(flags, int):
-        return flags
-    return parse_tick_flags(flags)
+97
def resolve_history_tick_flags(flags: int | str) -> int:
+    """Resolve tick copy flags from an integer or name.
+
+    Returns:
+        Integer tick flag value.
+    """
+    return parse_tick_flags(flags)
 
@@ -3793,8 +3791,7 @@ fails.

Source code in mt5cli/history.py -
71
-72
+              
72
 73
 74
 75
@@ -3809,23 +3806,24 @@ fails.

84 85 86 -87
def resolve_history_timeframes(
-    timeframes: Sequence[int | str] | None,
-) -> list[int]:
-    """Resolve rate timeframes, deduplicating aliases for the same integer.
-
-    Returns:
-        Ordered list of unique timeframe integers.
-    """
-    raw = timeframes if timeframes is not None else DEFAULT_HISTORY_TIMEFRAMES
-    seen: set[int] = set()
-    resolved: list[int] = []
-    for value in raw:
-        tf = value if isinstance(value, int) else parse_timeframe(str(value))
-        if tf not in seen:
-            seen.add(tf)
-            resolved.append(tf)
-    return resolved
+87
+88
def resolve_history_timeframes(
+    timeframes: Sequence[int | str] | None,
+) -> list[int]:
+    """Resolve rate timeframes, deduplicating aliases for the same integer.
+
+    Returns:
+        Ordered list of unique timeframe integers.
+    """
+    raw = timeframes if timeframes is not None else DEFAULT_HISTORY_TIMEFRAMES
+    seen: set[int] = set()
+    resolved: list[int] = []
+    for value in raw:
+        tf = parse_timeframe(value)
+        if tf not in seen:
+            seen.add(tf)
+            resolved.append(tf)
+    return resolved
 
diff --git a/api/sdk/index.html b/api/sdk/index.html index d9e1859..eafd8e1 100644 --- a/api/sdk/index.html +++ b/api/sdk/index.html @@ -747,7 +747,11 @@ clients are reused as-is and are not initialized or shut down.

Source code in mt5cli/sdk.py -
380
+                    
376
+377
+378
+379
+380
 381
 382
 383
@@ -774,42 +778,38 @@ clients are reused as-is and are not initialized or shut down.

404 405 406 -407 -408 -409 -410 -411
def __init__(
-    self,
-    *,
-    path: str | None = None,
-    login: int | None = None,
-    password: str | None = None,
-    server: str | None = None,
-    timeout: int | None = None,
-    config: Mt5Config | None = None,
-    client: Mt5DataClient | None = None,
-) -> None:
-    """Initialize the SDK client.
-
-    Args:
-        path: Path to MetaTrader5 terminal EXE file.
-        login: Trading account login.
-        password: Trading account password.
-        server: Trading server name.
-        timeout: Connection timeout in milliseconds.
-        config: Optional pre-built ``Mt5Config`` (overrides other args).
-        client: Optional already-connected ``Mt5DataClient``. Injected
-            clients are reused as-is and are not initialized or shut down.
-    """
-    self._config = config or build_config(
-        path=path,
-        login=login,
-        password=password,
-        server=server,
-        timeout=timeout,
-    )
-    self._client = client
-    self._owns_client = client is None
+407
def __init__(
+    self,
+    *,
+    path: str | None = None,
+    login: int | None = None,
+    password: str | None = None,
+    server: str | None = None,
+    timeout: int | None = None,
+    config: Mt5Config | None = None,
+    client: Mt5DataClient | None = None,
+) -> None:
+    """Initialize the SDK client.
+
+    Args:
+        path: Path to MetaTrader5 terminal EXE file.
+        login: Trading account login.
+        password: Trading account password.
+        server: Trading server name.
+        timeout: Connection timeout in milliseconds.
+        config: Optional pre-built ``Mt5Config`` (overrides other args).
+        client: Optional already-connected ``Mt5DataClient``. Injected
+            clients are reused as-is and are not initialized or shut down.
+    """
+    self._config = config or build_config(
+        path=path,
+        login=login,
+        password=password,
+        server=server,
+        timeout=timeout,
+    )
+    self._client = client
+    self._owns_client = client is None
 
@@ -891,7 +891,11 @@ clients are reused as-is and are not initialized or shut down.

Source code in mt5cli/sdk.py -
430
+              
426
+427
+428
+429
+430
 431
 432
 433
@@ -903,27 +907,23 @@ clients are reused as-is and are not initialized or shut down.

439 440 441 -442 -443 -444 -445 -446
def __enter__(self) -> Self:
-    """Open a persistent MT5 connection for multiple calls.
-
-    Returns:
-        This client instance.
-    """
-    if self._client is not None:
-        return self
-    client = Mt5DataClient(config=self._config)
-    try:
-        client.initialize_and_login_mt5()
-    except Exception:
-        client.shutdown()
-        raise
-    self._client = client
-    self._owns_client = True  # only set when this method created the client
-    return self
+442
def __enter__(self) -> Self:
+    """Open a persistent MT5 connection for multiple calls.
+
+    Returns:
+        This client instance.
+    """
+    if self._client is not None:
+        return self
+    client = Mt5DataClient(config=self._config)
+    try:
+        client.initialize_and_login_mt5()
+    except Exception:
+        client.shutdown()
+        raise
+    self._client = client
+    self._owns_client = True  # only set when this method created the client
+    return self
 
@@ -952,25 +952,25 @@ clients are reused as-is and are not initialized or shut down.

Source code in mt5cli/sdk.py -
448
+              
def __exit__(
-    self,
-    exc_type: type[BaseException] | None,
-    exc: BaseException | None,
-    tb: object,
-) -> None:
-    """Shut down the persistent MT5 connection."""
-    if self._client is not None and self._owns_client:
-        self._client.shutdown()
-        self._client = None
+453
def __exit__(
+    self,
+    exc_type: type[BaseException] | None,
+    exc: BaseException | None,
+    tb: object,
+) -> None:
+    """Shut down the persistent MT5 connection."""
+    if self._client is not None and self._owns_client:
+        self._client.shutdown()
+        self._client = None
 
@@ -995,11 +995,11 @@ clients are reused as-is and are not initialized or shut down.

Source code in mt5cli/sdk.py -
def account_info(self) -> pd.DataFrame:
-    """Return account information."""
-    return self._fetch(lambda c: c.account_info_as_df())
+              
def account_info(self) -> pd.DataFrame:
+    """Return account information."""
+    return self._fetch(lambda c: c.account_info_as_df())
 
@@ -1076,7 +1076,11 @@ clients are reused as-is and are not initialized or shut down.

Source code in mt5cli/sdk.py -
515
+              
511
+512
+513
+514
+515
 516
 517
 518
@@ -1107,46 +1111,42 @@ clients are reused as-is and are not initialized or shut down.

543 544 545 -546 -547 -548 -549 -550
def collect_latest_rates(
-    self,
-    symbols: Sequence[str],
-    timeframes: Sequence[int | str],
-    *,
-    count: int,
-    start_pos: int = 0,
-) -> dict[tuple[str, int], pd.DataFrame]:
-    """Return latest rates for each symbol/timeframe pair.
-
-    Returns:
-        Mapping keyed by ``(symbol, timeframe_int)``.
-
-    Raises:
-        ValueError: If ``count`` is not positive or inputs are empty.
-    """
-    _require_positive(count, "count")
-    if not symbols:
-        msg = "At least one symbol is required."
-        raise ValueError(msg)
-    if not timeframes:
-        msg = "At least one timeframe is required."
-        raise ValueError(msg)
-    resolved_timeframes = [_coerce_timeframe(timeframe) for timeframe in timeframes]
-    return self._fetch_value(
-        lambda c: {
-            (symbol, timeframe): c.copy_rates_from_pos_as_df(
-                symbol=symbol,
-                timeframe=timeframe,
-                start_pos=start_pos,
-                count=count,
-            )
-            for symbol in symbols
-            for timeframe in resolved_timeframes
-        },
-    )
+546
def collect_latest_rates(
+    self,
+    symbols: Sequence[str],
+    timeframes: Sequence[int | str],
+    *,
+    count: int,
+    start_pos: int = 0,
+) -> dict[tuple[str, int], pd.DataFrame]:
+    """Return latest rates for each symbol/timeframe pair.
+
+    Returns:
+        Mapping keyed by ``(symbol, timeframe_int)``.
+
+    Raises:
+        ValueError: If ``count`` is not positive or inputs are empty.
+    """
+    _require_positive(count, "count")
+    if not symbols:
+        msg = "At least one symbol is required."
+        raise ValueError(msg)
+    if not timeframes:
+        msg = "At least one timeframe is required."
+        raise ValueError(msg)
+    resolved_timeframes = [_coerce_timeframe(timeframe) for timeframe in timeframes]
+    return self._fetch_value(
+        lambda c: {
+            (symbol, timeframe): c.copy_rates_from_pos_as_df(
+                symbol=symbol,
+                timeframe=timeframe,
+                start_pos=start_pos,
+                count=count,
+            )
+            for symbol in symbols
+            for timeframe in resolved_timeframes
+        },
+    )
 
@@ -1176,7 +1176,11 @@ clients are reused as-is and are not initialized or shut down.

Source code in mt5cli/sdk.py -
467
+              
463
+464
+465
+466
+467
 468
 469
 470
@@ -1189,28 +1193,24 @@ clients are reused as-is and are not initialized or shut down.

477 478 479 -480 -481 -482 -483 -484
def copy_rates_from(
-    self,
-    symbol: str,
-    timeframe: int | str,
-    date_from: datetime | str,
-    count: int,
-) -> pd.DataFrame:
-    """Return rates starting from a date."""
-    tf = _coerce_timeframe(timeframe)
-    start = _require_datetime(date_from)
-    return self._fetch(
-        lambda c: c.copy_rates_from_as_df(
-            symbol=symbol,
-            timeframe=tf,
-            date_from=start,
-            count=count,
-        ),
-    )
+480
def copy_rates_from(
+    self,
+    symbol: str,
+    timeframe: int | str,
+    date_from: datetime | str,
+    count: int,
+) -> pd.DataFrame:
+    """Return rates starting from a date."""
+    tf = _coerce_timeframe(timeframe)
+    start = _require_datetime(date_from)
+    return self._fetch(
+        lambda c: c.copy_rates_from_as_df(
+            symbol=symbol,
+            timeframe=tf,
+            date_from=start,
+            count=count,
+        ),
+    )
 
@@ -1240,7 +1240,11 @@ clients are reused as-is and are not initialized or shut down.

Source code in mt5cli/sdk.py -
486
+              
482
+483
+484
+485
+486
 487
 488
 489
@@ -1252,27 +1256,23 @@ clients are reused as-is and are not initialized or shut down.

495 496 497 -498 -499 -500 -501 -502
def copy_rates_from_pos(
-    self,
-    symbol: str,
-    timeframe: int | str,
-    start_pos: int,
-    count: int,
-) -> pd.DataFrame:
-    """Return rates starting from a bar position."""
-    tf = _coerce_timeframe(timeframe)
-    return self._fetch(
-        lambda c: c.copy_rates_from_pos_as_df(
-            symbol=symbol,
-            timeframe=tf,
-            start_pos=start_pos,
-            count=count,
-        ),
-    )
+498
def copy_rates_from_pos(
+    self,
+    symbol: str,
+    timeframe: int | str,
+    start_pos: int,
+    count: int,
+) -> pd.DataFrame:
+    """Return rates starting from a bar position."""
+    tf = _coerce_timeframe(timeframe)
+    return self._fetch(
+        lambda c: c.copy_rates_from_pos_as_df(
+            symbol=symbol,
+            timeframe=tf,
+            start_pos=start_pos,
+            count=count,
+        ),
+    )
 
@@ -1302,7 +1302,11 @@ clients are reused as-is and are not initialized or shut down.

Source code in mt5cli/sdk.py -
552
+              
548
+549
+550
+551
+552
 553
 554
 555
@@ -1316,29 +1320,25 @@ clients are reused as-is and are not initialized or shut down.

563 564 565 -566 -567 -568 -569 -570
def copy_rates_range(
-    self,
-    symbol: str,
-    timeframe: int | str,
-    date_from: datetime | str,
-    date_to: datetime | str,
-) -> pd.DataFrame:
-    """Return rates for a date range."""
-    tf = _coerce_timeframe(timeframe)
-    start = _require_datetime(date_from)
-    end = _require_datetime(date_to)
-    return self._fetch(
-        lambda c: c.copy_rates_range_as_df(
-            symbol=symbol,
-            timeframe=tf,
-            date_from=start,
-            date_to=end,
-        ),
-    )
+566
def copy_rates_range(
+    self,
+    symbol: str,
+    timeframe: int | str,
+    date_from: datetime | str,
+    date_to: datetime | str,
+) -> pd.DataFrame:
+    """Return rates for a date range."""
+    tf = _coerce_timeframe(timeframe)
+    start = _require_datetime(date_from)
+    end = _require_datetime(date_to)
+    return self._fetch(
+        lambda c: c.copy_rates_range_as_df(
+            symbol=symbol,
+            timeframe=tf,
+            date_from=start,
+            date_to=end,
+        ),
+    )
 
@@ -1368,7 +1368,11 @@ clients are reused as-is and are not initialized or shut down.

Source code in mt5cli/sdk.py -
572
+              
568
+569
+570
+571
+572
 573
 574
 575
@@ -1381,28 +1385,24 @@ clients are reused as-is and are not initialized or shut down.

582 583 584 -585 -586 -587 -588 -589
def copy_ticks_from(
-    self,
-    symbol: str,
-    date_from: datetime | str,
-    count: int,
-    flags: int | str,
-) -> pd.DataFrame:
-    """Return ticks starting from a date."""
-    start = _require_datetime(date_from)
-    tick_flags = _coerce_tick_flags(flags)
-    return self._fetch(
-        lambda c: c.copy_ticks_from_as_df(
-            symbol=symbol,
-            date_from=start,
-            count=count,
-            flags=tick_flags,
-        ),
-    )
+585
def copy_ticks_from(
+    self,
+    symbol: str,
+    date_from: datetime | str,
+    count: int,
+    flags: int | str,
+) -> pd.DataFrame:
+    """Return ticks starting from a date."""
+    start = _require_datetime(date_from)
+    tick_flags = _coerce_tick_flags(flags)
+    return self._fetch(
+        lambda c: c.copy_ticks_from_as_df(
+            symbol=symbol,
+            date_from=start,
+            count=count,
+            flags=tick_flags,
+        ),
+    )
 
@@ -1432,7 +1432,11 @@ clients are reused as-is and are not initialized or shut down.

Source code in mt5cli/sdk.py -
591
+              
587
+588
+589
+590
+591
 592
 593
 594
@@ -1446,29 +1450,25 @@ clients are reused as-is and are not initialized or shut down.

602 603 604 -605 -606 -607 -608 -609
def copy_ticks_range(
-    self,
-    symbol: str,
-    date_from: datetime | str,
-    date_to: datetime | str,
-    flags: int | str,
-) -> pd.DataFrame:
-    """Return ticks for a date range."""
-    start = _require_datetime(date_from)
-    end = _require_datetime(date_to)
-    tick_flags = _coerce_tick_flags(flags)
-    return self._fetch(
-        lambda c: c.copy_ticks_range_as_df(
-            symbol=symbol,
-            date_from=start,
-            date_to=end,
-            flags=tick_flags,
-        ),
-    )
+605
def copy_ticks_range(
+    self,
+    symbol: str,
+    date_from: datetime | str,
+    date_to: datetime | str,
+    flags: int | str,
+) -> pd.DataFrame:
+    """Return ticks for a date range."""
+    start = _require_datetime(date_from)
+    end = _require_datetime(date_to)
+    tick_flags = _coerce_tick_flags(flags)
+    return self._fetch(
+        lambda c: c.copy_ticks_range_as_df(
+            symbol=symbol,
+            date_from=start,
+            date_to=end,
+            flags=tick_flags,
+        ),
+    )
 
@@ -1522,27 +1522,27 @@ injected client, including when used as a context manager.

Source code in mt5cli/sdk.py -
413
+              
@classmethod
-def from_connected_client(cls, client: Mt5DataClient) -> Self:
-    """Bind to an already-connected ``Mt5DataClient`` without owning it.
-
-    The returned ``Mt5CliClient`` never initializes or shuts down the
-    injected client, including when used as a context manager.
-
-    Returns:
-        Client wrapper bound to the injected connection.
-    """
-    return cls(client=client)
+419
@classmethod
+def from_connected_client(cls, client: Mt5DataClient) -> Self:
+    """Bind to an already-connected ``Mt5DataClient`` without owning it.
+
+    The returned ``Mt5CliClient`` never initializes or shuts down the
+    injected client, including when used as a context manager.
+
+    Returns:
+        Client wrapper bound to the injected connection.
+    """
+    return cls(client=client)
 
@@ -1574,7 +1574,11 @@ injected client, including when used as a context manager.

Source code in mt5cli/sdk.py -
680
+              
676
+677
+678
+679
+680
 681
 682
 683
@@ -1591,32 +1595,28 @@ injected client, including when used as a context manager.

694 695 696 -697 -698 -699 -700 -701
def history_deals(
-    self,
-    date_from: datetime | str | None = None,
-    date_to: datetime | str | None = None,
-    group: str | None = None,
-    symbol: str | None = None,
-    ticket: int | None = None,
-    position: int | None = None,
-) -> pd.DataFrame:
-    """Return historical deals."""
-    start = _coerce_datetime(date_from)
-    end = _coerce_datetime(date_to)
-    return self._fetch(
-        lambda c: c.history_deals_get_as_df(
-            date_from=start,
-            date_to=end,
-            group=group,
-            symbol=symbol,
-            ticket=ticket,
-            position=position,
-        ),
-    )
+697
def history_deals(
+    self,
+    date_from: datetime | str | None = None,
+    date_to: datetime | str | None = None,
+    group: str | None = None,
+    symbol: str | None = None,
+    ticket: int | None = None,
+    position: int | None = None,
+) -> pd.DataFrame:
+    """Return historical deals."""
+    start = _coerce_datetime(date_from)
+    end = _coerce_datetime(date_to)
+    return self._fetch(
+        lambda c: c.history_deals_get_as_df(
+            date_from=start,
+            date_to=end,
+            group=group,
+            symbol=symbol,
+            ticket=ticket,
+            position=position,
+        ),
+    )
 
@@ -1648,7 +1648,11 @@ injected client, including when used as a context manager.

Source code in mt5cli/sdk.py -
657
+              
653
+654
+655
+656
+657
 658
 659
 660
@@ -1665,32 +1669,28 @@ injected client, including when used as a context manager.

671 672 673 -674 -675 -676 -677 -678
def history_orders(
-    self,
-    date_from: datetime | str | None = None,
-    date_to: datetime | str | None = None,
-    group: str | None = None,
-    symbol: str | None = None,
-    ticket: int | None = None,
-    position: int | None = None,
-) -> pd.DataFrame:
-    """Return historical orders."""
-    start = _coerce_datetime(date_from)
-    end = _coerce_datetime(date_to)
-    return self._fetch(
-        lambda c: c.history_orders_get_as_df(
-            date_from=start,
-            date_to=end,
-            group=group,
-            symbol=symbol,
-            ticket=ticket,
-            position=position,
-        ),
-    )
+674
def history_orders(
+    self,
+    date_from: datetime | str | None = None,
+    date_to: datetime | str | None = None,
+    group: str | None = None,
+    symbol: str | None = None,
+    ticket: int | None = None,
+    position: int | None = None,
+) -> pd.DataFrame:
+    """Return historical orders."""
+    start = _coerce_datetime(date_from)
+    end = _coerce_datetime(date_to)
+    return self._fetch(
+        lambda c: c.history_orders_get_as_df(
+            date_from=start,
+            date_to=end,
+            group=group,
+            symbol=symbol,
+            ticket=ticket,
+            position=position,
+        ),
+    )
 
@@ -1715,11 +1715,11 @@ injected client, including when used as a context manager.

Source code in mt5cli/sdk.py -
def last_error(self) -> pd.DataFrame:
-    """Return the last error information."""
-    return self._fetch(lambda c: c.last_error_as_df())
+              
def last_error(self) -> pd.DataFrame:
+    """Return the last error information."""
+    return self._fetch(lambda c: c.last_error_as_df())
 
@@ -1749,25 +1749,25 @@ injected client, including when used as a context manager.

Source code in mt5cli/sdk.py -
504
+              
def latest_rates(
-    self,
-    symbol: str,
-    timeframe: int | str,
-    count: int,
-    start_pos: int = 0,
-) -> pd.DataFrame:
-    """Return the latest rates from a bar position."""
-    _require_positive(count, "count")
-    return self.copy_rates_from_pos(symbol, timeframe, start_pos, count)
+509
def latest_rates(
+    self,
+    symbol: str,
+    timeframe: int | str,
+    count: int,
+    start_pos: int = 0,
+) -> pd.DataFrame:
+    """Return the latest rates from a bar position."""
+    _require_positive(count, "count")
+    return self.copy_rates_from_pos(symbol, timeframe, start_pos, count)
 
@@ -1792,11 +1792,11 @@ injected client, including when used as a context manager.

Source code in mt5cli/sdk.py -
def market_book(self, symbol: str) -> pd.DataFrame:
-    """Return market depth for a symbol."""
-    return self._fetch(lambda c: c.market_book_get_as_df(symbol=symbol))
+              
def market_book(self, symbol: str) -> pd.DataFrame:
+    """Return market depth for a symbol."""
+    return self._fetch(lambda c: c.market_book_get_as_df(symbol=symbol))
 
@@ -1885,27 +1885,27 @@ injected client, including when used as a context manager.

Source code in mt5cli/sdk.py -
776
+              
def minimum_margins(self, symbol: str) -> pd.DataFrame:
-    """Return minimum-volume buy and sell margin requirements.
-
-    Args:
-        symbol: Symbol name.
-
-    Returns:
-        One-row DataFrame with columns ``symbol``, ``account_currency``,
-        ``volume_min``, ``buy_margin``, and ``sell_margin``.
-    """
-    return self._fetch(lambda c: _fetch_minimum_margins(c, symbol))
+782
def minimum_margins(self, symbol: str) -> pd.DataFrame:
+    """Return minimum-volume buy and sell margin requirements.
+
+    Args:
+        symbol: Symbol name.
+
+    Returns:
+        One-row DataFrame with columns ``symbol``, ``account_currency``,
+        ``volume_min``, ``buy_margin``, and ``sell_margin``.
+    """
+    return self._fetch(lambda c: _fetch_minimum_margins(c, symbol))
 
@@ -1930,7 +1930,11 @@ injected client, including when used as a context manager.

Source code in mt5cli/sdk.py -
788
+              
784
+785
+786
+787
+788
 789
 790
 791
@@ -1945,30 +1949,26 @@ injected client, including when used as a context manager.

800 801 802 -803 -804 -805 -806 -807
def mt5_summary(self) -> dict[str, object]:
-    """Return a compact terminal/account status summary."""
-
-    def _summary(client: Mt5DataClient) -> dict[str, object]:
-        return {
-            "version": _plain_mt5_value(
-                _call_required_client_method(client, "version"),
-            ),
-            "terminal_info": _plain_mt5_value(
-                _call_required_client_method(client, "terminal_info"),
-            ),
-            "account_info": _plain_mt5_value(
-                _call_required_client_method(client, "account_info"),
-            ),
-            "symbols_total": _plain_mt5_value(
-                _call_required_client_method(client, "symbols_total"),
-            ),
-        }
-
-    return self._fetch_value(_summary)
+803
def mt5_summary(self) -> dict[str, object]:
+    """Return a compact terminal/account status summary."""
+
+    def _summary(client: Mt5DataClient) -> dict[str, object]:
+        return {
+            "version": _plain_mt5_value(
+                _call_required_client_method(client, "version"),
+            ),
+            "terminal_info": _plain_mt5_value(
+                _call_required_client_method(client, "terminal_info"),
+            ),
+            "account_info": _plain_mt5_value(
+                _call_required_client_method(client, "account_info"),
+            ),
+            "symbols_total": _plain_mt5_value(
+                _call_required_client_method(client, "symbols_total"),
+            ),
+        }
+
+    return self._fetch_value(_summary)
 
@@ -1993,27 +1993,27 @@ injected client, including when used as a context manager.

Source code in mt5cli/sdk.py -
809
+              
def mt5_summary_as_df(self) -> pd.DataFrame:
-    """Return an export-safe one-row terminal/account summary DataFrame."""
-    summary = self.mt5_summary()
-    return pd.DataFrame(
-        [
-            {
-                key: _mt5_summary_export_value(value)
-                for key, value in summary.items()
-            },
-        ],
-    )
+815
def mt5_summary_as_df(self) -> pd.DataFrame:
+    """Return an export-safe one-row terminal/account summary DataFrame."""
+    summary = self.mt5_summary()
+    return pd.DataFrame(
+        [
+            {
+                key: _mt5_summary_export_value(value)
+                for key, value in summary.items()
+            },
+        ],
+    )
 
@@ -2042,7 +2042,11 @@ injected client, including when used as a context manager.

Source code in mt5cli/sdk.py -
627
+              
623
+624
+625
+626
+627
 628
 629
 630
@@ -2051,24 +2055,20 @@ injected client, including when used as a context manager.

633 634 635 -636 -637 -638 -639 -640
def orders(
-    self,
-    symbol: str | None = None,
-    group: str | None = None,
-    ticket: int | None = None,
-) -> pd.DataFrame:
-    """Return active orders."""
-    return self._fetch(
-        lambda c: c.orders_get_as_df(
-            symbol=symbol,
-            group=group,
-            ticket=ticket,
-        ),
-    )
+636
def orders(
+    self,
+    symbol: str | None = None,
+    group: str | None = None,
+    ticket: int | None = None,
+) -> pd.DataFrame:
+    """Return active orders."""
+    return self._fetch(
+        lambda c: c.orders_get_as_df(
+            symbol=symbol,
+            group=group,
+            ticket=ticket,
+        ),
+    )
 
@@ -2097,7 +2097,11 @@ injected client, including when used as a context manager.

Source code in mt5cli/sdk.py -
642
+              
638
+639
+640
+641
+642
 643
 644
 645
@@ -2106,24 +2110,20 @@ injected client, including when used as a context manager.

648 649 650 -651 -652 -653 -654 -655
def positions(
-    self,
-    symbol: str | None = None,
-    group: str | None = None,
-    ticket: int | None = None,
-) -> pd.DataFrame:
-    """Return open positions."""
-    return self._fetch(
-        lambda c: c.positions_get_as_df(
-            symbol=symbol,
-            group=group,
-            ticket=ticket,
-        ),
-    )
+651
def positions(
+    self,
+    symbol: str | None = None,
+    group: str | None = None,
+    ticket: int | None = None,
+) -> pd.DataFrame:
+    """Return open positions."""
+    return self._fetch(
+        lambda c: c.positions_get_as_df(
+            symbol=symbol,
+            group=group,
+            ticket=ticket,
+        ),
+    )
 
@@ -2153,7 +2153,11 @@ injected client, including when used as a context manager.

Source code in mt5cli/sdk.py -
703
+              
699
+700
+701
+702
+703
 704
 705
 706
@@ -2165,27 +2169,23 @@ injected client, including when used as a context manager.

712 713 714 -715 -716 -717 -718 -719
def recent_history_deals(
-    self,
-    hours: float,
-    date_to: datetime | str | None = None,
-    group: str | None = None,
-    symbol: str | None = None,
-) -> pd.DataFrame:
-    """Return historical deals from a recent trailing window."""
-    _require_positive(hours, "hours")
-    end = _require_datetime(date_to) if date_to is not None else datetime.now(UTC)
-    start = end - timedelta(hours=hours)
-    return self.history_deals(
-        date_from=start,
-        date_to=end,
-        group=group,
-        symbol=symbol,
-    )
+715
def recent_history_deals(
+    self,
+    hours: float,
+    date_to: datetime | str | None = None,
+    group: str | None = None,
+    symbol: str | None = None,
+) -> pd.DataFrame:
+    """Return historical deals from a recent trailing window."""
+    _require_positive(hours, "hours")
+    end = _require_datetime(date_to) if date_to is not None else datetime.now(UTC)
+    start = end - timedelta(hours=hours)
+    return self.history_deals(
+        date_from=start,
+        date_to=end,
+        group=group,
+        symbol=symbol,
+    )
 
@@ -2349,7 +2349,11 @@ fetching the entire range.

Source code in mt5cli/sdk.py -
737
+              
733
+734
+735
+736
+737
 738
 739
 740
@@ -2382,48 +2386,44 @@ fetching the entire range.

767 768 769 -770 -771 -772 -773 -774
def recent_ticks(
-    self,
-    symbol: str,
-    seconds: float,
-    *,
-    date_to: datetime | str | None = None,
-    count: int = 10000,
-    flags: int | str = "ALL",
-) -> pd.DataFrame:
-    """Return ticks from a recent time window.
-
-    Args:
-        symbol: Symbol name.
-        seconds: Lookback window in seconds ending at ``date_to``.
-        date_to: Window end time. When ``None``, uses the latest
-            ``symbol_info_tick().time`` rather than wall-clock now.
-        count: Maximum ticks to return. Values ``<= 0`` return the full
-            window without trimming. Positive values keep the most recent
-            ticks; when the window is sparse, ``copy_ticks_from`` avoids
-            fetching the entire range.
-        flags: Tick flags as ``ALL``, ``INFO``, ``TRADE``, or an integer.
-
-    Returns:
-        Tick DataFrame with MT5 tick columns such as ``time``, ``bid``,
-        ``ask``, ``last``, and ``volume``.
-    """
-    tick_flags = _coerce_tick_flags(flags)
-    end = _coerce_datetime(date_to)
-    return self._fetch(
-        lambda c: _fetch_recent_ticks(
-            c,
-            symbol,
-            seconds,
-            end,
-            count,
-            tick_flags,
-        ),
-    )
+770
def recent_ticks(
+    self,
+    symbol: str,
+    seconds: float,
+    *,
+    date_to: datetime | str | None = None,
+    count: int = 10000,
+    flags: int | str = "ALL",
+) -> pd.DataFrame:
+    """Return ticks from a recent time window.
+
+    Args:
+        symbol: Symbol name.
+        seconds: Lookback window in seconds ending at ``date_to``.
+        date_to: Window end time. When ``None``, uses the latest
+            ``symbol_info_tick().time`` rather than wall-clock now.
+        count: Maximum ticks to return. Values ``<= 0`` return the full
+            window without trimming. Positive values keep the most recent
+            ticks; when the window is sparse, ``copy_ticks_from`` avoids
+            fetching the entire range.
+        flags: Tick flags as ``ALL``, ``INFO``, ``TRADE``, or an integer.
+
+    Returns:
+        Tick DataFrame with MT5 tick columns such as ``time``, ``bid``,
+        ``ask``, ``last``, and ``volume``.
+    """
+    tick_flags = _coerce_tick_flags(flags)
+    end = _coerce_datetime(date_to)
+    return self._fetch(
+        lambda c: _fetch_recent_ticks(
+            c,
+            symbol,
+            seconds,
+            end,
+            count,
+            tick_flags,
+        ),
+    )
 
@@ -2448,11 +2448,11 @@ fetching the entire range.

Source code in mt5cli/sdk.py -
def symbol_info(self, symbol: str) -> pd.DataFrame:
-    """Return details for one symbol."""
-    return self._fetch(lambda c: c.symbol_info_as_df(symbol=symbol))
+              
def symbol_info(self, symbol: str) -> pd.DataFrame:
+    """Return details for one symbol."""
+    return self._fetch(lambda c: c.symbol_info_as_df(symbol=symbol))
 
@@ -2477,11 +2477,11 @@ fetching the entire range.

Source code in mt5cli/sdk.py -
def symbol_info_tick(self, symbol: str) -> pd.DataFrame:
-    """Return the last tick for a symbol."""
-    return self._fetch(lambda c: c.symbol_info_tick_as_df(symbol=symbol))
+              
def symbol_info_tick(self, symbol: str) -> pd.DataFrame:
+    """Return the last tick for a symbol."""
+    return self._fetch(lambda c: c.symbol_info_tick_as_df(symbol=symbol))
 
@@ -2506,11 +2506,11 @@ fetching the entire range.

Source code in mt5cli/sdk.py -
def symbols(self, group: str | None = None) -> pd.DataFrame:
-    """Return the symbol list."""
-    return self._fetch(lambda c: c.symbols_get_as_df(group=group))
+              
def symbols(self, group: str | None = None) -> pd.DataFrame:
+    """Return the symbol list."""
+    return self._fetch(lambda c: c.symbols_get_as_df(group=group))
 
@@ -2535,11 +2535,11 @@ fetching the entire range.

Source code in mt5cli/sdk.py -
def terminal_info(self) -> pd.DataFrame:
-    """Return terminal information."""
-    return self._fetch(lambda c: c.terminal_info_as_df())
+              
def terminal_info(self) -> pd.DataFrame:
+    """Return terminal information."""
+    return self._fetch(lambda c: c.terminal_info_as_df())
 
@@ -2564,11 +2564,11 @@ fetching the entire range.

Source code in mt5cli/sdk.py -
def version(self) -> pd.DataFrame:
-    """Return MetaTrader5 version information."""
-    return self._fetch(lambda c: c.version_as_df())
+              
def version(self) -> pd.DataFrame:
+    """Return MetaTrader5 version information."""
+    return self._fetch(lambda c: c.version_as_df())
 
@@ -2796,7 +2796,11 @@ propagate so callers control logging.

Source code in mt5cli/sdk.py -
1046
+                    
1042
+1043
+1044
+1045
+1046
 1047
 1048
 1049
@@ -2837,56 +2841,52 @@ propagate so callers control logging.

1084 1085 1086 -1087 -1088 -1089 -1090 -1091
def __init__(
-    self,
-    *,
-    output: Path | str,
-    datasets: set[Dataset] | None = None,
-    timeframes: Sequence[int | str] | None = None,
-    flags: int | str = "ALL",
-    lookback_hours: float = 24.0,
-    with_views: bool = False,
-    include_account_events: bool = True,
-    interval_seconds: float = 0.0,
-    suppress_errors: bool = False,
-) -> None:
-    """Initialize the throttled updater.
-
-    Args:
-        output: SQLite database path.
-        datasets: Datasets to include (defaults to all).
-        timeframes: Rate timeframes to update (defaults to all fixed MT5
-            timeframes).
-        flags: Tick copy flags as integer or name (e.g. ``ALL``).
-        lookback_hours: First-run lookback when a table has no prior rows.
-        with_views: Create ``cash_events`` and ``positions_reconstructed``
-            views.
-        include_account_events: Include account-level cash events.
-        interval_seconds: Minimum seconds between successful updates. Values
-            ``<= 0`` update on every call.
-        suppress_errors: When True, recoverable errors (``Mt5TradingError``,
-            ``Mt5RuntimeError``, ``sqlite3.Error``, ``ValueError``,
-            ``OSError``, and MT5 client capability ``AttributeError`` /
-            ``TypeError`` for history API methods) raised during an update
-            are swallowed and :meth:`update` returns False without advancing
-            the throttle. Other ``AttributeError`` / ``TypeError`` values
-            always propagate. When False (default), recoverable errors
-            propagate so callers control logging.
-    """
-    self.output = output
-    self.datasets = datasets
-    self.timeframes = timeframes
-    self.flags = flags
-    self.lookback_hours = lookback_hours
-    self.with_views = with_views
-    self.include_account_events = include_account_events
-    self.interval_seconds = interval_seconds
-    self.suppress_errors = suppress_errors
-    self._last_update_monotonic: float | None = None
+1087
def __init__(
+    self,
+    *,
+    output: Path | str,
+    datasets: set[Dataset] | None = None,
+    timeframes: Sequence[int | str] | None = None,
+    flags: int | str = "ALL",
+    lookback_hours: float = 24.0,
+    with_views: bool = False,
+    include_account_events: bool = True,
+    interval_seconds: float = 0.0,
+    suppress_errors: bool = False,
+) -> None:
+    """Initialize the throttled updater.
+
+    Args:
+        output: SQLite database path.
+        datasets: Datasets to include (defaults to all).
+        timeframes: Rate timeframes to update (defaults to all fixed MT5
+            timeframes).
+        flags: Tick copy flags as integer or name (e.g. ``ALL``).
+        lookback_hours: First-run lookback when a table has no prior rows.
+        with_views: Create ``cash_events`` and ``positions_reconstructed``
+            views.
+        include_account_events: Include account-level cash events.
+        interval_seconds: Minimum seconds between successful updates. Values
+            ``<= 0`` update on every call.
+        suppress_errors: When True, recoverable errors (``Mt5TradingError``,
+            ``Mt5RuntimeError``, ``sqlite3.Error``, ``ValueError``,
+            ``OSError``, and MT5 client capability ``AttributeError`` /
+            ``TypeError`` for history API methods) raised during an update
+            are swallowed and :meth:`update` returns False without advancing
+            the throttle. Other ``AttributeError`` / ``TypeError`` values
+            always propagate. When False (default), recoverable errors
+            propagate so callers control logging.
+    """
+    self.output = output
+    self.datasets = datasets
+    self.timeframes = timeframes
+    self.flags = flags
+    self.lookback_hours = lookback_hours
+    self.with_views = with_views
+    self.include_account_events = include_account_events
+    self.interval_seconds = interval_seconds
+    self.suppress_errors = suppress_errors
+    self._last_update_monotonic: float | None = None
 
@@ -3186,27 +3186,27 @@ propagate so callers control logging.

Source code in mt5cli/sdk.py -
1098
+              
def should_update(self) -> bool:
-    """Return whether enough time has elapsed to run another update.
-
-    Returns:
-        True when ``interval_seconds <= 0``, when no update has succeeded
-        yet, or when at least ``interval_seconds`` have elapsed since the
-        last successful update.
-    """
-    if self.interval_seconds <= 0 or self._last_update_monotonic is None:
-        return True
-    return (time.monotonic() - self._last_update_monotonic) >= self.interval_seconds
+1104
def should_update(self) -> bool:
+    """Return whether enough time has elapsed to run another update.
+
+    Returns:
+        True when ``interval_seconds <= 0``, when no update has succeeded
+        yet, or when at least ``interval_seconds`` have elapsed since the
+        last successful update.
+    """
+    if self.interval_seconds <= 0 or self._last_update_monotonic is None:
+        return True
+    return (time.monotonic() - self._last_update_monotonic) >= self.interval_seconds
 
@@ -3368,7 +3368,11 @@ is False, or any other type error.

Source code in mt5cli/sdk.py -
1110
+              
1106
+1107
+1108
+1109
+1110
 1111
 1112
 1113
@@ -3417,64 +3421,60 @@ is False, or any other type error.

1156 1157 1158 -1159 -1160 -1161 -1162 -1163
def update(self, client: Mt5DataClient, symbols: Sequence[str]) -> bool:
-    """Run a throttled incremental history update.
+1159
def update(self, client: Mt5DataClient, symbols: Sequence[str]) -> bool:
+    """Run a throttled incremental history update.
+
+    Args:
+        client: Connected MT5 data client.
+        symbols: Symbols to update.
 
-    Args:
-        client: Connected MT5 data client.
-        symbols: Symbols to update.
-
-    Returns:
-        True if an update ran successfully, False if it was throttled or
-        (when ``suppress_errors`` is True) failed with a recoverable error.
-        When ``suppress_errors`` is False, recoverable update failures
-        propagate to the caller.
-
-    Raises:
-        AttributeError: MT5 client capability mismatch when
-            ``suppress_errors`` is False, or any other attribute error.
-        TypeError: MT5 client capability mismatch when ``suppress_errors``
-            is False, or any other type error.
-    """
-    if not self.should_update():
-        return False
-    try:
-        _resolve_update_history_request(
-            output=self.output,
-            symbols=symbols,
-            datasets=self.datasets,
-            timeframes=self.timeframes,
-            flags=self.flags,
-            lookback_hours=self.lookback_hours,
-            date_to=None,
-        )
-        update_history(
-            client=client,
-            output=self.output,
-            symbols=symbols,
-            datasets=self.datasets,
-            timeframes=self.timeframes,
-            flags=self.flags,
-            lookback_hours=self.lookback_hours,
-            with_views=self.with_views,
-            include_account_events=self.include_account_events,
-        )
-    except _RECOVERABLE_HISTORY_UPDATE_ERRORS:
-        if self.suppress_errors:
-            logger.warning("Suppressed history update error", exc_info=True)
-            return False
-        raise
-    except (AttributeError, TypeError) as exc:
-        if self.suppress_errors and _is_mt5_client_capability_error(exc):
-            logger.warning("Suppressed history update error", exc_info=True)
-            return False
-        raise
-    self._last_update_monotonic = time.monotonic()
-    return True
+    Returns:
+        True if an update ran successfully, False if it was throttled or
+        (when ``suppress_errors`` is True) failed with a recoverable error.
+        When ``suppress_errors`` is False, recoverable update failures
+        propagate to the caller.
+
+    Raises:
+        AttributeError: MT5 client capability mismatch when
+            ``suppress_errors`` is False, or any other attribute error.
+        TypeError: MT5 client capability mismatch when ``suppress_errors``
+            is False, or any other type error.
+    """
+    if not self.should_update():
+        return False
+    try:
+        _resolve_update_history_request(
+            output=self.output,
+            symbols=symbols,
+            datasets=self.datasets,
+            timeframes=self.timeframes,
+            flags=self.flags,
+            lookback_hours=self.lookback_hours,
+            date_to=None,
+        )
+        update_history(
+            client=client,
+            output=self.output,
+            symbols=symbols,
+            datasets=self.datasets,
+            timeframes=self.timeframes,
+            flags=self.flags,
+            lookback_hours=self.lookback_hours,
+            with_views=self.with_views,
+            include_account_events=self.include_account_events,
+        )
+    except _RECOVERABLE_HISTORY_UPDATE_ERRORS:
+        if self.suppress_errors:
+            logger.warning("Suppressed history update error", exc_info=True)
+            return False
+        raise
+    except (AttributeError, TypeError) as exc:
+        if self.suppress_errors and _is_mt5_client_capability_error(exc):
+            logger.warning("Suppressed history update error", exc_info=True)
+            return False
+        raise
+    self._last_update_monotonic = time.monotonic()
+    return True
 
@@ -3510,11 +3510,11 @@ is False, or any other type error.

Source code in mt5cli/sdk.py -
def account_info(*, config: Mt5Config | None = None) -> pd.DataFrame:
-    """Return account information."""
-    return _make_client(config=config).account_info()
+              
def account_info(*, config: Mt5Config | None = None) -> pd.DataFrame:
+    """Return account information."""
+    return _make_client(config=config).account_info()
 
@@ -3569,7 +3569,11 @@ is False, or any other type error.

Source code in mt5cli/sdk.py -
300
+              
296
+297
+298
+299
+300
 301
 302
 303
@@ -3584,30 +3588,26 @@ is False, or any other type error.

312 313 314 -315 -316 -317 -318 -319
def build_config(
-    *,
-    path: str | None = None,
-    login: int | None = None,
-    password: str | None = None,
-    server: str | None = None,
-    timeout: int | None = None,
-) -> Mt5Config:
-    """Build an ``Mt5Config`` from optional connection parameters.
-
-    Returns:
-        Configured ``Mt5Config`` instance.
-    """
-    return Mt5Config(
-        path=path,
-        login=login,
-        password=password,
-        server=server,
-        timeout=timeout,
-    )
+315
def build_config(
+    *,
+    path: str | None = None,
+    login: int | None = None,
+    password: str | None = None,
+    server: str | None = None,
+    timeout: int | None = None,
+) -> Mt5Config:
+    """Build an ``Mt5Config`` from optional connection parameters.
+
+    Returns:
+        Configured ``Mt5Config`` instance.
+    """
+    return Mt5Config(
+        path=path,
+        login=login,
+        password=password,
+        server=server,
+        timeout=timeout,
+    )
 
@@ -3630,7 +3630,7 @@ is False, or any other type error.

*, datasets: set[Dataset] | None = None, timeframe: int | str = 1, - flags: int | str = 1, + flags: int | str = "ALL", if_exists: IfExists = @@ -3831,7 +3831,11 @@ is False, or any other type error.

Source code in mt5cli/sdk.py -
1166
+              
1162
+1163
+1164
+1165
+1166
 1167
 1168
 1169
@@ -3890,74 +3894,70 @@ is False, or any other type error.

1222 1223 1224 -1225 -1226 -1227 -1228 -1229
def collect_history(
-    output: Path,
-    symbols: list[str],
-    date_from: datetime | str,
-    date_to: datetime | str,
-    *,
-    datasets: set[Dataset] | None = None,
-    timeframe: int | str = 1,
-    flags: int | str = 1,
-    if_exists: IfExists = IfExists.FAIL,
-    with_views: bool = False,
-    config: Mt5Config | None = None,
-) -> None:
-    """Collect historical datasets into a single SQLite database.
-
-    Args:
-        output: SQLite database path.
-        symbols: Symbols to collect.
-        date_from: Start date.
-        date_to: End date.
-        datasets: Datasets to include (defaults to all).
-        timeframe: Rates timeframe as integer or name (e.g. ``M1``).
-        flags: Tick copy flags as integer or name (e.g. ``ALL``).
-        if_exists: Behavior when a target table already exists.
-        with_views: Create ``cash_events`` and ``positions_reconstructed`` views.
-        config: MT5 connection configuration.
-    """
-    start = _require_datetime(date_from)
-    end = _require_datetime(date_to)
-    selected = datasets if datasets is not None else set(Dataset)
-    tf = _coerce_timeframe(timeframe)
-    tick_flags = _coerce_tick_flags(flags)
-    mt5_config = config or build_config()
-    with _connected_client(mt5_config) as client, sqlite3.connect(output) as conn:
-        conn.execute("PRAGMA journal_mode=WAL")
-        conn.execute("PRAGMA synchronous=NORMAL")
-        written_tables, written_columns = write_collected_datasets(
-            conn,
-            client,
-            symbols,
-            selected,
-            tf,
-            tick_flags,
-            start,
-            end,
-            if_exists,
-        )
-        create_history_indexes(conn, written_columns)
-        if with_views and Dataset.history_deals in written_tables:
-            create_cash_events_view(conn, written_columns[Dataset.history_deals])
-            create_positions_reconstructed_view(
-                conn,
-                written_columns[Dataset.history_deals],
+1225
def collect_history(
+    output: Path,
+    symbols: list[str],
+    date_from: datetime | str,
+    date_to: datetime | str,
+    *,
+    datasets: set[Dataset] | None = None,
+    timeframe: int | str = 1,
+    flags: int | str = "ALL",
+    if_exists: IfExists = IfExists.FAIL,
+    with_views: bool = False,
+    config: Mt5Config | None = None,
+) -> None:
+    """Collect historical datasets into a single SQLite database.
+
+    Args:
+        output: SQLite database path.
+        symbols: Symbols to collect.
+        date_from: Start date.
+        date_to: End date.
+        datasets: Datasets to include (defaults to all).
+        timeframe: Rates timeframe as integer or name (e.g. ``M1``).
+        flags: Tick copy flags as integer or name (e.g. ``ALL``).
+        if_exists: Behavior when a target table already exists.
+        with_views: Create ``cash_events`` and ``positions_reconstructed`` views.
+        config: MT5 connection configuration.
+    """
+    start = _require_datetime(date_from)
+    end = _require_datetime(date_to)
+    selected = datasets if datasets is not None else set(Dataset)
+    tf = _coerce_timeframe(timeframe)
+    tick_flags = _coerce_tick_flags(flags)
+    mt5_config = config or build_config()
+    with _connected_client(mt5_config) as client, sqlite3.connect(output) as conn:
+        conn.execute("PRAGMA journal_mode=WAL")
+        conn.execute("PRAGMA synchronous=NORMAL")
+        written_tables, written_columns = write_collected_datasets(
+            conn,
+            client,
+            symbols,
+            selected,
+            tf,
+            tick_flags,
+            start,
+            end,
+            if_exists,
+        )
+        create_history_indexes(conn, written_columns)
+        if with_views and Dataset.history_deals in written_tables:
+            create_cash_events_view(conn, written_columns[Dataset.history_deals])
+            create_positions_reconstructed_view(
+                conn,
+                written_columns[Dataset.history_deals],
+            )
+        elif with_views:
+            logger.warning(
+                "--with-views ignored: history_deals table was not written",
             )
-        elif with_views:
-            logger.warning(
-                "--with-views ignored: history_deals table was not written",
-            )
-    logger.info(
-        "Collected %s for %d symbol(s) into %s",
-        ", ".join(sorted(ds.value for ds in selected)),
-        len(symbols),
-        output,
-    )
+    logger.info(
+        "Collected %s for %d symbol(s) into %s",
+        ", ".join(sorted(ds.value for ds in selected)),
+        len(symbols),
+        output,
+    )
 
@@ -4166,7 +4166,11 @@ disables retries.

Source code in mt5cli/sdk.py -
1685
+              
1681
+1682
+1683
+1684
+1685
 1686
 1687
 1688
@@ -4205,54 +4209,50 @@ disables retries.

1721 1722 1723 -1724 -1725 -1726 -1727 -1728
def collect_latest_closed_rates_by_granularity(
-    accounts: Sequence[AccountSpec],
-    granularities: Sequence[int | str],
-    count: int,
-    *,
-    start_pos: int = 0,
-    base_config: Mt5Config | None = None,
-    retry_count: int = 0,
-    backoff_base: float = 2.0,
-) -> dict[tuple[str, str], pd.DataFrame]:
-    """Collect latest closed rate bars keyed by symbol and granularity name.
+1724
def collect_latest_closed_rates_by_granularity(
+    accounts: Sequence[AccountSpec],
+    granularities: Sequence[int | str],
+    count: int,
+    *,
+    start_pos: int = 0,
+    base_config: Mt5Config | None = None,
+    retry_count: int = 0,
+    backoff_base: float = 2.0,
+) -> dict[tuple[str, str], pd.DataFrame]:
+    """Collect latest closed rate bars keyed by symbol and granularity name.
+
+    Thin wrapper around :func:`collect_latest_closed_rates_for_accounts` that
+    rekeys the result by granularity name (for example ``M1``) instead of the
+    integer timeframe.
 
-    Thin wrapper around :func:`collect_latest_closed_rates_for_accounts` that
-    rekeys the result by granularity name (for example ``M1``) instead of the
-    integer timeframe.
-
-    Args:
-        accounts: Account groups to read. Each must define at least one symbol.
-        granularities: MT5 timeframes as integers or names (for example ``M1``).
-        count: Number of closed bars to return per symbol/timeframe.
-        start_pos: Initial bar position offset passed to the underlying collector.
-        base_config: Optional base configuration whose fields fill any value not
-            set on an individual account.
-        retry_count: Maximum number of retries after the first attempt. ``0``
-            disables retries.
-        backoff_base: Base for exponential backoff between retry attempts.
-
-    Returns:
-        Mapping keyed by ``(symbol, granularity_name)``. Propagates
-        ``ValueError`` from :func:`collect_latest_closed_rates_for_accounts`.
-    """
-    loaded = collect_latest_closed_rates_for_accounts(
-        accounts,
-        granularities,
-        count,
-        start_pos=start_pos,
-        base_config=base_config,
-        retry_count=retry_count,
-        backoff_base=backoff_base,
-    )
-    return {
-        (symbol, resolve_granularity_name(timeframe)): frame
-        for (symbol, timeframe), frame in loaded.items()
-    }
+    Args:
+        accounts: Account groups to read. Each must define at least one symbol.
+        granularities: MT5 timeframes as integers or names (for example ``M1``).
+        count: Number of closed bars to return per symbol/timeframe.
+        start_pos: Initial bar position offset passed to the underlying collector.
+        base_config: Optional base configuration whose fields fill any value not
+            set on an individual account.
+        retry_count: Maximum number of retries after the first attempt. ``0``
+            disables retries.
+        backoff_base: Base for exponential backoff between retry attempts.
+
+    Returns:
+        Mapping keyed by ``(symbol, granularity_name)``. Propagates
+        ``ValueError`` from :func:`collect_latest_closed_rates_for_accounts`.
+    """
+    loaded = collect_latest_closed_rates_for_accounts(
+        accounts,
+        granularities,
+        count,
+        start_pos=start_pos,
+        base_config=base_config,
+        retry_count=retry_count,
+        backoff_base=backoff_base,
+    )
+    return {
+        (symbol, resolve_granularity_name(timeframe)): frame
+        for (symbol, timeframe), frame in loaded.items()
+    }
 
@@ -4480,7 +4480,11 @@ dropping the still-forming bar when start_pos is 0). Source code in mt5cli/sdk.py -
1622
+              
1618
+1619
+1620
+1621
+1622
 1623
 1624
 1625
@@ -4536,71 +4540,67 @@ dropping the still-forming bar when start_pos is 0).1675
 1676
 1677
-1678
-1679
-1680
-1681
-1682
def collect_latest_closed_rates_for_accounts(
-    accounts: Sequence[AccountSpec],
-    timeframes: Sequence[int | str],
-    count: int,
-    *,
-    start_pos: int = 0,
-    base_config: Mt5Config | None = None,
-    retry_count: int = 0,
-    backoff_base: float = 2.0,
-) -> dict[tuple[str, int], pd.DataFrame]:
-    """Collect latest closed rate bars across multiple MT5 account groups.
-
-    When ``start_pos`` is ``0`` (the default), MetaTrader 5 includes the
-    still-forming current bar as the last row. This helper fetches
-    ``count + 1`` bars, drops that bar with :func:`drop_forming_rate_bar`, and
-    validates that each resulting frame is non-empty. When ``start_pos`` is
-    greater than zero the forming bar is not in range, so only ``count`` bars
-    are fetched and no row is dropped.
-
-    Wraps :func:`collect_latest_rates_for_accounts_with_retries` for transient
-    MT5 error handling.
-
-    Args:
-        accounts: Account groups to read. Each must define at least one symbol.
-        timeframes: MT5 timeframes as integers or names (for example ``M1``).
-        count: Number of closed bars to return per symbol/timeframe.
-        start_pos: Initial bar position offset passed to the underlying collector.
-        base_config: Optional base configuration whose fields fill any value not
-            set on an individual account.
-        retry_count: Maximum number of retries after the first attempt. ``0``
-            disables retries.
-        backoff_base: Base for exponential backoff between retry attempts.
-
-    Returns:
-        Mapping keyed by ``(symbol, timeframe_int)``.
-
-    Raises:
-        ValueError: If inputs are invalid, or any series is empty (after
-            dropping the still-forming bar when ``start_pos`` is ``0``).
-    """
-    _require_positive(count, "count")
-    _require_non_negative(start_pos, "start_pos")
-    fetch_count = count + 1 if start_pos == 0 else count
-    loaded = collect_latest_rates_for_accounts_with_retries(
-        accounts,
-        timeframes,
-        fetch_count,
-        start_pos=start_pos,
-        base_config=base_config,
-        retry_count=retry_count,
-        backoff_base=backoff_base,
-    )
-    result: dict[tuple[str, int], pd.DataFrame] = {}
-    for key, df_rate in loaded.items():
-        closed = drop_forming_rate_bar(df_rate) if start_pos == 0 else df_rate
-        if closed.empty:
-            symbol, timeframe = key
-            msg = f"Rate data is empty for {symbol!r} at timeframe {timeframe}."
-            raise ValueError(msg)
-        result[key] = closed
-    return result
+1678
def collect_latest_closed_rates_for_accounts(
+    accounts: Sequence[AccountSpec],
+    timeframes: Sequence[int | str],
+    count: int,
+    *,
+    start_pos: int = 0,
+    base_config: Mt5Config | None = None,
+    retry_count: int = 0,
+    backoff_base: float = 2.0,
+) -> dict[tuple[str, int], pd.DataFrame]:
+    """Collect latest closed rate bars across multiple MT5 account groups.
+
+    When ``start_pos`` is ``0`` (the default), MetaTrader 5 includes the
+    still-forming current bar as the last row. This helper fetches
+    ``count + 1`` bars, drops that bar with :func:`drop_forming_rate_bar`, and
+    validates that each resulting frame is non-empty. When ``start_pos`` is
+    greater than zero the forming bar is not in range, so only ``count`` bars
+    are fetched and no row is dropped.
+
+    Wraps :func:`collect_latest_rates_for_accounts_with_retries` for transient
+    MT5 error handling.
+
+    Args:
+        accounts: Account groups to read. Each must define at least one symbol.
+        timeframes: MT5 timeframes as integers or names (for example ``M1``).
+        count: Number of closed bars to return per symbol/timeframe.
+        start_pos: Initial bar position offset passed to the underlying collector.
+        base_config: Optional base configuration whose fields fill any value not
+            set on an individual account.
+        retry_count: Maximum number of retries after the first attempt. ``0``
+            disables retries.
+        backoff_base: Base for exponential backoff between retry attempts.
+
+    Returns:
+        Mapping keyed by ``(symbol, timeframe_int)``.
+
+    Raises:
+        ValueError: If inputs are invalid, or any series is empty (after
+            dropping the still-forming bar when ``start_pos`` is ``0``).
+    """
+    _require_positive(count, "count")
+    _require_non_negative(start_pos, "start_pos")
+    fetch_count = count + 1 if start_pos == 0 else count
+    loaded = collect_latest_rates_for_accounts_with_retries(
+        accounts,
+        timeframes,
+        fetch_count,
+        start_pos=start_pos,
+        base_config=base_config,
+        retry_count=retry_count,
+        backoff_base=backoff_base,
+    )
+    result: dict[tuple[str, int], pd.DataFrame] = {}
+    for key, df_rate in loaded.items():
+        closed = drop_forming_rate_bar(df_rate) if start_pos == 0 else df_rate
+        if closed.empty:
+            symbol, timeframe = key
+            msg = f"Rate data is empty for {symbol!r} at timeframe {timeframe}."
+            raise ValueError(msg)
+        result[key] = closed
+    return result
 
@@ -4632,7 +4632,11 @@ dropping the still-forming bar when start_pos is 0). Source code in mt5cli/sdk.py -
1287
+              
1283
+1284
+1285
+1286
+1287
 1288
 1289
 1290
@@ -4642,25 +4646,21 @@ dropping the still-forming bar when start_pos is 0).1294
 1295
 1296
-1297
-1298
-1299
-1300
-1301
def collect_latest_rates(
-    symbols: Sequence[str],
-    timeframes: Sequence[int | str],
-    *,
-    count: int,
-    start_pos: int = 0,
-    config: Mt5Config | None = None,
-) -> dict[tuple[str, int], pd.DataFrame]:
-    """Return latest rates for each symbol/timeframe pair."""
-    return _make_client(config=config).collect_latest_rates(
-        symbols,
-        timeframes,
-        count=count,
-        start_pos=start_pos,
-    )
+1297
def collect_latest_rates(
+    symbols: Sequence[str],
+    timeframes: Sequence[int | str],
+    *,
+    count: int,
+    start_pos: int = 0,
+    config: Mt5Config | None = None,
+) -> dict[tuple[str, int], pd.DataFrame]:
+    """Return latest rates for each symbol/timeframe pair."""
+    return _make_client(config=config).collect_latest_rates(
+        symbols,
+        timeframes,
+        count=count,
+        start_pos=start_pos,
+    )
 
@@ -4857,7 +4857,11 @@ empty, or count is not positive.

Source code in mt5cli/sdk.py -
1505
+              
1501
+1502
+1503
+1504
+1505
 1506
 1507
 1508
@@ -4905,63 +4909,59 @@ empty, or count is not positive.

1550 1551 1552 -1553 -1554 -1555 -1556 -1557
def collect_latest_rates_for_accounts(
-    accounts: Sequence[AccountSpec],
-    timeframes: Sequence[int | str],
-    count: int,
-    *,
-    start_pos: int = 0,
-    base_config: Mt5Config | None = None,
-) -> dict[tuple[str, int], pd.DataFrame]:
-    """Collect latest rates across multiple MT5 account groups.
-
-    Each account is connected in turn, its symbols are read for every
-    timeframe, and the resulting frames are merged into a single mapping.
-
-    Args:
-        accounts: Account groups to read. Each must define at least one symbol.
-        timeframes: MT5 timeframes as integers or names (for example ``M1``).
-        count: Number of most recent bars to read per symbol/timeframe.
-        start_pos: Initial bar position offset.
-        base_config: Optional base configuration whose fields fill any value not
-            set on an individual account.
+1553
def collect_latest_rates_for_accounts(
+    accounts: Sequence[AccountSpec],
+    timeframes: Sequence[int | str],
+    count: int,
+    *,
+    start_pos: int = 0,
+    base_config: Mt5Config | None = None,
+) -> dict[tuple[str, int], pd.DataFrame]:
+    """Collect latest rates across multiple MT5 account groups.
+
+    Each account is connected in turn, its symbols are read for every
+    timeframe, and the resulting frames are merged into a single mapping.
+
+    Args:
+        accounts: Account groups to read. Each must define at least one symbol.
+        timeframes: MT5 timeframes as integers or names (for example ``M1``).
+        count: Number of most recent bars to read per symbol/timeframe.
+        start_pos: Initial bar position offset.
+        base_config: Optional base configuration whose fields fill any value not
+            set on an individual account.
+
+    Returns:
+        Mapping keyed by ``(symbol, timeframe_int)``. When accounts share a
+        symbol/timeframe pair, the last account processed wins.
 
-    Returns:
-        Mapping keyed by ``(symbol, timeframe_int)``. When accounts share a
-        symbol/timeframe pair, the last account processed wins.
-
-    Raises:
-        ValueError: If ``accounts``, ``timeframes``, or any account's symbols are
-            empty, or ``count`` is not positive.
-    """
-    account_list = list(accounts)
-    if not account_list:
-        msg = "At least one account is required."
-        raise ValueError(msg)
-    if not timeframes:
-        msg = "At least one timeframe is required."
-        raise ValueError(msg)
-    if any(not account.symbols for account in account_list):
-        msg = "Each account requires at least one symbol."
-        raise ValueError(msg)
-    _require_positive(count, "count")
-    result: dict[tuple[str, int], pd.DataFrame] = {}
-    for account in account_list:
-        config = _build_account_config(account, base_config)
-        with Mt5CliClient(config=config) as client:
-            result.update(
-                client.collect_latest_rates(
-                    account.symbols,
-                    timeframes,
-                    count=count,
-                    start_pos=start_pos,
-                ),
-            )
-    return result
+    Raises:
+        ValueError: If ``accounts``, ``timeframes``, or any account's symbols are
+            empty, or ``count`` is not positive.
+    """
+    account_list = list(accounts)
+    if not account_list:
+        msg = "At least one account is required."
+        raise ValueError(msg)
+    if not timeframes:
+        msg = "At least one timeframe is required."
+        raise ValueError(msg)
+    if any(not account.symbols for account in account_list):
+        msg = "Each account requires at least one symbol."
+        raise ValueError(msg)
+    _require_positive(count, "count")
+    result: dict[tuple[str, int], pd.DataFrame] = {}
+    for account in account_list:
+        config = _build_account_config(account, base_config)
+        with Mt5CliClient(config=config) as client:
+            result.update(
+                client.collect_latest_rates(
+                    account.symbols,
+                    timeframes,
+                    count=count,
+                    start_pos=start_pos,
+                ),
+            )
+    return result
 
@@ -5192,7 +5192,11 @@ attempt n (1-indexed) is backoff_base ** n seconds.

Source code in mt5cli/sdk.py -
1560
+              
1556
+1557
+1558
+1559
+1560
 1561
 1562
 1563
@@ -5247,70 +5251,66 @@ attempt n (1-indexed) is backoff_base ** n seconds.

1612 1613 1614 -1615 -1616 -1617 -1618 -1619
def collect_latest_rates_for_accounts_with_retries(
-    accounts: Sequence[AccountSpec],
-    timeframes: Sequence[int | str],
-    count: int,
-    *,
-    start_pos: int = 0,
-    base_config: Mt5Config | None = None,
-    retry_count: int = 0,
-    backoff_base: float = 2.0,
-) -> dict[tuple[str, int], pd.DataFrame]:
-    """Collect latest rates across accounts, retrying transient MT5 failures.
-
-    Wraps :func:`collect_latest_rates_for_accounts` with bounded exponential
-    backoff. Only ``pdmt5.Mt5TradingError`` and ``pdmt5.Mt5RuntimeError`` are
-    retried; other exceptions propagate immediately. The final failure is
-    re-raised once retries are exhausted.
-
-    Args:
-        accounts: Account groups to read. Each must define at least one symbol.
-        timeframes: MT5 timeframes as integers or names (for example ``M1``).
-        count: Number of most recent bars to read per symbol/timeframe.
-        start_pos: Initial bar position offset.
-        base_config: Optional base configuration whose fields fill any value not
-            set on an individual account.
-        retry_count: Maximum number of retries after the first attempt. ``0``
-            disables retries.
-        backoff_base: Base for exponential backoff. The delay before retry
-            attempt ``n`` (1-indexed) is ``backoff_base ** n`` seconds.
-
-    Returns:
-        Mapping keyed by ``(symbol, timeframe_int)``. Propagates ``ValueError``
-        for invalid inputs (see :func:`collect_latest_rates_for_accounts`) and
-        re-raises the last ``pdmt5.Mt5TradingError`` or ``pdmt5.Mt5RuntimeError``
-        once retries are exhausted.
-    """
-    attempts = max(retry_count, 0) + 1
-
-    def _collect() -> dict[tuple[str, int], pd.DataFrame]:
-        return collect_latest_rates_for_accounts(
-            accounts,
-            timeframes,
-            count,
-            start_pos=start_pos,
-            base_config=base_config,
-        )
-
-    for attempt in range(attempts - 1):
-        try:
-            return _collect()
-        except (Mt5TradingError, Mt5RuntimeError) as exc:
-            delay = backoff_base ** (attempt + 1)
-            logger.warning(
-                "Rate collection failed (attempt %d/%d): %s; retrying in %.1fs",
-                attempt + 1,
-                attempts,
-                exc,
-                delay,
-            )
-            time.sleep(delay)
-    return _collect()
+1615
def collect_latest_rates_for_accounts_with_retries(
+    accounts: Sequence[AccountSpec],
+    timeframes: Sequence[int | str],
+    count: int,
+    *,
+    start_pos: int = 0,
+    base_config: Mt5Config | None = None,
+    retry_count: int = 0,
+    backoff_base: float = 2.0,
+) -> dict[tuple[str, int], pd.DataFrame]:
+    """Collect latest rates across accounts, retrying transient MT5 failures.
+
+    Wraps :func:`collect_latest_rates_for_accounts` with bounded exponential
+    backoff. Only ``pdmt5.Mt5TradingError`` and ``pdmt5.Mt5RuntimeError`` are
+    retried; other exceptions propagate immediately. The final failure is
+    re-raised once retries are exhausted.
+
+    Args:
+        accounts: Account groups to read. Each must define at least one symbol.
+        timeframes: MT5 timeframes as integers or names (for example ``M1``).
+        count: Number of most recent bars to read per symbol/timeframe.
+        start_pos: Initial bar position offset.
+        base_config: Optional base configuration whose fields fill any value not
+            set on an individual account.
+        retry_count: Maximum number of retries after the first attempt. ``0``
+            disables retries.
+        backoff_base: Base for exponential backoff. The delay before retry
+            attempt ``n`` (1-indexed) is ``backoff_base ** n`` seconds.
+
+    Returns:
+        Mapping keyed by ``(symbol, timeframe_int)``. Propagates ``ValueError``
+        for invalid inputs (see :func:`collect_latest_rates_for_accounts`) and
+        re-raises the last ``pdmt5.Mt5TradingError`` or ``pdmt5.Mt5RuntimeError``
+        once retries are exhausted.
+    """
+    attempts = max(retry_count, 0) + 1
+
+    def _collect() -> dict[tuple[str, int], pd.DataFrame]:
+        return collect_latest_rates_for_accounts(
+            accounts,
+            timeframes,
+            count,
+            start_pos=start_pos,
+            base_config=base_config,
+        )
+
+    for attempt in range(attempts - 1):
+        try:
+            return _collect()
+        except (Mt5TradingError, Mt5RuntimeError) as exc:
+            delay = backoff_base ** (attempt + 1)
+            logger.warning(
+                "Rate collection failed (attempt %d/%d): %s; retrying in %.1fs",
+                attempt + 1,
+                attempts,
+                exc,
+                delay,
+            )
+            time.sleep(delay)
+    return _collect()
 
@@ -5342,7 +5342,11 @@ attempt n (1-indexed) is backoff_base ** n seconds.

Source code in mt5cli/sdk.py -
1236
+              
1232
+1233
+1234
+1235
+1236
 1237
 1238
 1239
@@ -5352,25 +5356,21 @@ attempt n (1-indexed) is backoff_base ** n seconds.

1243 1244 1245 -1246 -1247 -1248 -1249 -1250
def copy_rates_from(
-    symbol: str,
-    timeframe: int | str,
-    date_from: datetime | str,
-    count: int,
-    *,
-    config: Mt5Config | None = None,
-) -> pd.DataFrame:
-    """Return rates starting from a date."""
-    return _make_client(config=config).copy_rates_from(
-        symbol,
-        timeframe,
-        date_from,
-        count,
-    )
+1246
def copy_rates_from(
+    symbol: str,
+    timeframe: int | str,
+    date_from: datetime | str,
+    count: int,
+    *,
+    config: Mt5Config | None = None,
+) -> pd.DataFrame:
+    """Return rates starting from a date."""
+    return _make_client(config=config).copy_rates_from(
+        symbol,
+        timeframe,
+        date_from,
+        count,
+    )
 
@@ -5402,7 +5402,11 @@ attempt n (1-indexed) is backoff_base ** n seconds.

Source code in mt5cli/sdk.py -
1253
+              
1249
+1250
+1251
+1252
+1253
 1254
 1255
 1256
@@ -5412,25 +5416,21 @@ attempt n (1-indexed) is backoff_base ** n seconds.

1260 1261 1262 -1263 -1264 -1265 -1266 -1267
def copy_rates_from_pos(
-    symbol: str,
-    timeframe: int | str,
-    start_pos: int,
-    count: int,
-    *,
-    config: Mt5Config | None = None,
-) -> pd.DataFrame:
-    """Return rates starting from a bar position."""
-    return _make_client(config=config).copy_rates_from_pos(
-        symbol,
-        timeframe,
-        start_pos,
-        count,
-    )
+1263
def copy_rates_from_pos(
+    symbol: str,
+    timeframe: int | str,
+    start_pos: int,
+    count: int,
+    *,
+    config: Mt5Config | None = None,
+) -> pd.DataFrame:
+    """Return rates starting from a bar position."""
+    return _make_client(config=config).copy_rates_from_pos(
+        symbol,
+        timeframe,
+        start_pos,
+        count,
+    )
 
@@ -5462,7 +5462,11 @@ attempt n (1-indexed) is backoff_base ** n seconds.

Source code in mt5cli/sdk.py -
1731
+              
1727
+1728
+1729
+1730
+1731
 1732
 1733
 1734
@@ -5472,25 +5476,21 @@ attempt n (1-indexed) is backoff_base ** n seconds.

1738 1739 1740 -1741 -1742 -1743 -1744 -1745
def copy_rates_range(
-    symbol: str,
-    timeframe: int | str,
-    date_from: datetime | str,
-    date_to: datetime | str,
-    *,
-    config: Mt5Config | None = None,
-) -> pd.DataFrame:
-    """Return rates for a date range."""
-    return _make_client(config=config).copy_rates_range(
-        symbol,
-        timeframe,
-        date_from,
-        date_to,
-    )
+1741
def copy_rates_range(
+    symbol: str,
+    timeframe: int | str,
+    date_from: datetime | str,
+    date_to: datetime | str,
+    *,
+    config: Mt5Config | None = None,
+) -> pd.DataFrame:
+    """Return rates for a date range."""
+    return _make_client(config=config).copy_rates_range(
+        symbol,
+        timeframe,
+        date_from,
+        date_to,
+    )
 
@@ -5522,7 +5522,11 @@ attempt n (1-indexed) is backoff_base ** n seconds.

Source code in mt5cli/sdk.py -
1748
+              
1744
+1745
+1746
+1747
+1748
 1749
 1750
 1751
@@ -5532,25 +5536,21 @@ attempt n (1-indexed) is backoff_base ** n seconds.

1755 1756 1757 -1758 -1759 -1760 -1761 -1762
def copy_ticks_from(
-    symbol: str,
-    date_from: datetime | str,
-    count: int,
-    flags: int | str,
-    *,
-    config: Mt5Config | None = None,
-) -> pd.DataFrame:
-    """Return ticks starting from a date."""
-    return _make_client(config=config).copy_ticks_from(
-        symbol,
-        date_from,
-        count,
-        flags,
-    )
+1758
def copy_ticks_from(
+    symbol: str,
+    date_from: datetime | str,
+    count: int,
+    flags: int | str,
+    *,
+    config: Mt5Config | None = None,
+) -> pd.DataFrame:
+    """Return ticks starting from a date."""
+    return _make_client(config=config).copy_ticks_from(
+        symbol,
+        date_from,
+        count,
+        flags,
+    )
 
@@ -5582,7 +5582,11 @@ attempt n (1-indexed) is backoff_base ** n seconds.

Source code in mt5cli/sdk.py -
1765
+              
1761
+1762
+1763
+1764
+1765
 1766
 1767
 1768
@@ -5592,25 +5596,21 @@ attempt n (1-indexed) is backoff_base ** n seconds.

1772 1773 1774 -1775 -1776 -1777 -1778 -1779
def copy_ticks_range(
-    symbol: str,
-    date_from: datetime | str,
-    date_to: datetime | str,
-    flags: int | str,
-    *,
-    config: Mt5Config | None = None,
-) -> pd.DataFrame:
-    """Return ticks for a date range."""
-    return _make_client(config=config).copy_ticks_range(
-        symbol,
-        date_from,
-        date_to,
-        flags,
-    )
+1775
def copy_ticks_range(
+    symbol: str,
+    date_from: datetime | str,
+    date_to: datetime | str,
+    flags: int | str,
+    *,
+    config: Mt5Config | None = None,
+) -> pd.DataFrame:
+    """Return ticks for a date range."""
+    return _make_client(config=config).copy_ticks_range(
+        symbol,
+        date_from,
+        date_to,
+        flags,
+    )
 
@@ -5644,7 +5644,11 @@ attempt n (1-indexed) is backoff_base ** n seconds.

Source code in mt5cli/sdk.py -
1861
+              
1857
+1858
+1859
+1860
+1861
 1862
 1863
 1864
@@ -5658,29 +5662,25 @@ attempt n (1-indexed) is backoff_base ** n seconds.

1872 1873 1874 -1875 -1876 -1877 -1878 -1879
def history_deals(
-    date_from: datetime | str | None = None,
-    date_to: datetime | str | None = None,
-    group: str | None = None,
-    symbol: str | None = None,
-    ticket: int | None = None,
-    position: int | None = None,
-    *,
-    config: Mt5Config | None = None,
-) -> pd.DataFrame:
-    """Return historical deals."""
-    return _make_client(config=config).history_deals(
-        date_from=date_from,
-        date_to=date_to,
-        group=group,
-        symbol=symbol,
-        ticket=ticket,
-        position=position,
-    )
+1875
def history_deals(
+    date_from: datetime | str | None = None,
+    date_to: datetime | str | None = None,
+    group: str | None = None,
+    symbol: str | None = None,
+    ticket: int | None = None,
+    position: int | None = None,
+    *,
+    config: Mt5Config | None = None,
+) -> pd.DataFrame:
+    """Return historical deals."""
+    return _make_client(config=config).history_deals(
+        date_from=date_from,
+        date_to=date_to,
+        group=group,
+        symbol=symbol,
+        ticket=ticket,
+        position=position,
+    )
 
@@ -5714,7 +5714,11 @@ attempt n (1-indexed) is backoff_base ** n seconds.

Source code in mt5cli/sdk.py -
1840
+              
1836
+1837
+1838
+1839
+1840
 1841
 1842
 1843
@@ -5728,29 +5732,25 @@ attempt n (1-indexed) is backoff_base ** n seconds.

1851 1852 1853 -1854 -1855 -1856 -1857 -1858
def history_orders(
-    date_from: datetime | str | None = None,
-    date_to: datetime | str | None = None,
-    group: str | None = None,
-    symbol: str | None = None,
-    ticket: int | None = None,
-    position: int | None = None,
-    *,
-    config: Mt5Config | None = None,
-) -> pd.DataFrame:
-    """Return historical orders."""
-    return _make_client(config=config).history_orders(
-        date_from=date_from,
-        date_to=date_to,
-        group=group,
-        symbol=symbol,
-        ticket=ticket,
-        position=position,
-    )
+1854
def history_orders(
+    date_from: datetime | str | None = None,
+    date_to: datetime | str | None = None,
+    group: str | None = None,
+    symbol: str | None = None,
+    ticket: int | None = None,
+    position: int | None = None,
+    *,
+    config: Mt5Config | None = None,
+) -> pd.DataFrame:
+    """Return historical orders."""
+    return _make_client(config=config).history_orders(
+        date_from=date_from,
+        date_to=date_to,
+        group=group,
+        symbol=symbol,
+        ticket=ticket,
+        position=position,
+    )
 
@@ -5775,11 +5775,11 @@ attempt n (1-indexed) is backoff_base ** n seconds.

Source code in mt5cli/sdk.py -
def last_error(*, config: Mt5Config | None = None) -> pd.DataFrame:
-    """Return the last error information."""
-    return _make_client(config=config).last_error()
+              
def last_error(*, config: Mt5Config | None = None) -> pd.DataFrame:
+    """Return the last error information."""
+    return _make_client(config=config).last_error()
 
@@ -5811,7 +5811,11 @@ attempt n (1-indexed) is backoff_base ** n seconds.

Source code in mt5cli/sdk.py -
1270
+              
1266
+1267
+1268
+1269
+1270
 1271
 1272
 1273
@@ -5821,25 +5825,21 @@ attempt n (1-indexed) is backoff_base ** n seconds.

1277 1278 1279 -1280 -1281 -1282 -1283 -1284
def latest_rates(
-    symbol: str,
-    timeframe: int | str,
-    count: int,
-    start_pos: int = 0,
-    *,
-    config: Mt5Config | None = None,
-) -> pd.DataFrame:
-    """Return the latest rates from a bar position."""
-    return _make_client(config=config).latest_rates(
-        symbol,
-        timeframe,
-        count,
-        start_pos=start_pos,
-    )
+1280
def latest_rates(
+    symbol: str,
+    timeframe: int | str,
+    count: int,
+    start_pos: int = 0,
+    *,
+    config: Mt5Config | None = None,
+) -> pd.DataFrame:
+    """Return the latest rates from a bar position."""
+    return _make_client(config=config).latest_rates(
+        symbol,
+        timeframe,
+        count,
+        start_pos=start_pos,
+    )
 
@@ -5866,19 +5866,19 @@ attempt n (1-indexed) is backoff_base ** n seconds.

Source code in mt5cli/sdk.py -
1918
+              
def market_book(
-    symbol: str,
-    *,
-    config: Mt5Config | None = None,
-) -> pd.DataFrame:
-    """Return market depth for a symbol."""
-    return _make_client(config=config).market_book(symbol)
+1920
def market_book(
+    symbol: str,
+    *,
+    config: Mt5Config | None = None,
+) -> pd.DataFrame:
+    """Return market depth for a symbol."""
+    return _make_client(config=config).market_book(symbol)
 
@@ -5906,25 +5906,25 @@ attempt n (1-indexed) is backoff_base ** n seconds.

Source code in mt5cli/sdk.py -
1949
+              
def minimum_margins(
-    symbol: str,
-    *,
-    config: Mt5Config | None = None,
-) -> pd.DataFrame:
-    """Return minimum-volume buy and sell margin requirements.
-
-    See ``Mt5CliClient.minimum_margins`` for return details.
-    """
-    return _make_client(config=config).minimum_margins(symbol)
+1954
def minimum_margins(
+    symbol: str,
+    *,
+    config: Mt5Config | None = None,
+) -> pd.DataFrame:
+    """Return minimum-volume buy and sell margin requirements.
+
+    See ``Mt5CliClient.minimum_margins`` for return details.
+    """
+    return _make_client(config=config).minimum_margins(symbol)
 
@@ -6009,7 +6009,11 @@ attaches to a running terminal.

Source code in mt5cli/sdk.py -
357
+              
353
+354
+355
+356
+357
 358
 359
 360
@@ -6022,28 +6026,24 @@ attaches to a running terminal.

367 368 369 -370 -371 -372 -373 -374
@contextmanager
-def mt5_session(config: Mt5Config | None = None) -> Iterator[Mt5CliClient]:
-    """Open an MT5 terminal session and yield a connected client.
+370
@contextmanager
+def mt5_session(config: Mt5Config | None = None) -> Iterator[Mt5CliClient]:
+    """Open an MT5 terminal session and yield a connected client.
+
+    Launches the MetaTrader 5 terminal using ``Mt5Config.path`` (when set),
+    logs in, yields a connected :class:`Mt5CliClient`, and always shuts the
+    terminal down on exit.
 
-    Launches the MetaTrader 5 terminal using ``Mt5Config.path`` (when set),
-    logs in, yields a connected :class:`Mt5CliClient`, and always shuts the
-    terminal down on exit.
+    Args:
+        config: MT5 connection configuration. Defaults to an empty config that
+            attaches to a running terminal.
 
-    Args:
-        config: MT5 connection configuration. Defaults to an empty config that
-            attaches to a running terminal.
-
-    Yields:
-        Connected ``Mt5CliClient`` bound to the session.
-    """
-    mt5_config = config or build_config()
-    with _connected_client(mt5_config) as client:
-        yield Mt5CliClient.from_connected_client(client)
+    Yields:
+        Connected ``Mt5CliClient`` bound to the session.
+    """
+    mt5_config = config or build_config()
+    with _connected_client(mt5_config) as client:
+        yield Mt5CliClient.from_connected_client(client)
 
@@ -6070,11 +6070,11 @@ attaches to a running terminal.

Source code in mt5cli/sdk.py -
def mt5_summary(*, config: Mt5Config | None = None) -> dict[str, object]:
-    """Return a compact terminal/account status summary."""
-    return _make_client(config=config).mt5_summary()
+              
def mt5_summary(*, config: Mt5Config | None = None) -> dict[str, object]:
+    """Return a compact terminal/account status summary."""
+    return _make_client(config=config).mt5_summary()
 
@@ -6101,11 +6101,11 @@ attaches to a running terminal.

Source code in mt5cli/sdk.py -
def mt5_summary_as_df(*, config: Mt5Config | None = None) -> pd.DataFrame:
-    """Return an export-safe terminal/account status summary DataFrame."""
-    return _make_client(config=config).mt5_summary_as_df()
+              
def mt5_summary_as_df(*, config: Mt5Config | None = None) -> pd.DataFrame:
+    """Return an export-safe terminal/account status summary DataFrame."""
+    return _make_client(config=config).mt5_summary_as_df()
 
@@ -6136,7 +6136,11 @@ attaches to a running terminal.

Source code in mt5cli/sdk.py -
1810
+              
1806
+1807
+1808
+1809
+1810
 1811
 1812
 1813
@@ -6144,23 +6148,19 @@ attaches to a running terminal.

1815 1816 1817 -1818 -1819 -1820 -1821 -1822
def orders(
-    symbol: str | None = None,
-    group: str | None = None,
-    ticket: int | None = None,
-    *,
-    config: Mt5Config | None = None,
-) -> pd.DataFrame:
-    """Return active orders."""
-    return _make_client(config=config).orders(
-        symbol=symbol,
-        group=group,
-        ticket=ticket,
-    )
+1818
def orders(
+    symbol: str | None = None,
+    group: str | None = None,
+    ticket: int | None = None,
+    *,
+    config: Mt5Config | None = None,
+) -> pd.DataFrame:
+    """Return active orders."""
+    return _make_client(config=config).orders(
+        symbol=symbol,
+        group=group,
+        ticket=ticket,
+    )
 
@@ -6191,7 +6191,11 @@ attaches to a running terminal.

Source code in mt5cli/sdk.py -
1825
+              
1821
+1822
+1823
+1824
+1825
 1826
 1827
 1828
@@ -6199,23 +6203,19 @@ attaches to a running terminal.

1830 1831 1832 -1833 -1834 -1835 -1836 -1837
def positions(
-    symbol: str | None = None,
-    group: str | None = None,
-    ticket: int | None = None,
-    *,
-    config: Mt5Config | None = None,
-) -> pd.DataFrame:
-    """Return open positions."""
-    return _make_client(config=config).positions(
-        symbol=symbol,
-        group=group,
-        ticket=ticket,
-    )
+1833
def positions(
+    symbol: str | None = None,
+    group: str | None = None,
+    ticket: int | None = None,
+    *,
+    config: Mt5Config | None = None,
+) -> pd.DataFrame:
+    """Return open positions."""
+    return _make_client(config=config).positions(
+        symbol=symbol,
+        group=group,
+        ticket=ticket,
+    )
 
@@ -6247,7 +6247,11 @@ attaches to a running terminal.

Source code in mt5cli/sdk.py -
1882
+              
1878
+1879
+1880
+1881
+1882
 1883
 1884
 1885
@@ -6257,25 +6261,21 @@ attaches to a running terminal.

1889 1890 1891 -1892 -1893 -1894 -1895 -1896
def recent_history_deals(
-    hours: float,
-    date_to: datetime | str | None = None,
-    group: str | None = None,
-    symbol: str | None = None,
-    *,
-    config: Mt5Config | None = None,
-) -> pd.DataFrame:
-    """Return historical deals from a recent trailing window."""
-    return _make_client(config=config).recent_history_deals(
-        hours,
-        date_to=date_to,
-        group=group,
-        symbol=symbol,
-    )
+1892
def recent_history_deals(
+    hours: float,
+    date_to: datetime | str | None = None,
+    group: str | None = None,
+    symbol: str | None = None,
+    *,
+    config: Mt5Config | None = None,
+) -> pd.DataFrame:
+    """Return historical deals from a recent trailing window."""
+    return _make_client(config=config).recent_history_deals(
+        hours,
+        date_to=date_to,
+        group=group,
+        symbol=symbol,
+    )
 
@@ -6309,7 +6309,11 @@ attaches to a running terminal.

Source code in mt5cli/sdk.py -
1927
+              
1923
+1924
+1925
+1926
+1927
 1928
 1929
 1930
@@ -6324,30 +6328,26 @@ attaches to a running terminal.

1939 1940 1941 -1942 -1943 -1944 -1945 -1946
def recent_ticks(
-    symbol: str,
-    seconds: float,
-    *,
-    date_to: datetime | str | None = None,
-    count: int = 10000,
-    flags: int | str = "ALL",
-    config: Mt5Config | None = None,
-) -> pd.DataFrame:
-    """Return ticks from a recent time window ending at ``date_to`` or now.
-
-    See ``Mt5CliClient.recent_ticks`` for parameter and return details.
-    """
-    return _make_client(config=config).recent_ticks(
-        symbol,
-        seconds,
-        date_to=date_to,
-        count=count,
-        flags=flags,
-    )
+1942
def recent_ticks(
+    symbol: str,
+    seconds: float,
+    *,
+    date_to: datetime | str | None = None,
+    count: int = 10000,
+    flags: int | str = "ALL",
+    config: Mt5Config | None = None,
+) -> pd.DataFrame:
+    """Return ticks from a recent time window ending at ``date_to`` or now.
+
+    See ``Mt5CliClient.recent_ticks`` for parameter and return details.
+    """
+    return _make_client(config=config).recent_ticks(
+        symbol,
+        seconds,
+        date_to=date_to,
+        count=count,
+        flags=flags,
+    )
 
@@ -6583,7 +6583,11 @@ substituted from the environment.

Source code in mt5cli/sdk.py -
1388
+              
1384
+1385
+1386
+1387
+1388
 1389
 1390
 1391
@@ -6616,48 +6620,44 @@ substituted from the environment.

1418 1419 1420 -1421 -1422 -1423 -1424 -1425
def resolve_account_spec(
-    account: AccountSpec,
-    *,
-    login: int | str | None = None,
-    password: str | None = None,
-    server: str | None = None,
-    path: str | None = None,
-    timeout: int | None = None,
-) -> AccountSpec:
-    """Resolve an account's credentials from overrides and ``${ENV_VAR}`` values.
-
-    Explicit override arguments take precedence over the corresponding
-    :class:`AccountSpec` fields. The resolved string fields (``login``,
-    ``password``, ``server``, ``path``) have any ``${ENV_VAR}`` placeholders
-    substituted from the environment.
-
-    Args:
-        account: Source account specification.
-        login: Optional explicit login override.
-        password: Optional explicit password override.
-        server: Optional explicit server override.
-        path: Optional explicit terminal path override.
-        timeout: Optional explicit connection timeout override.
-
-    Returns:
-        A new :class:`AccountSpec` with resolved credentials and the original
-        symbols preserved. Raises ``ValueError`` (via
-        :func:`substitute_env_placeholders`) if a referenced environment
-        variable is not set.
-    """
-    return AccountSpec(
-        symbols=account.symbols,
-        login=_resolve_login(login, account.login),
-        password=_resolve_field(password, account.password),
-        server=_resolve_field(server, account.server),
-        path=_resolve_field(path, account.path),
-        timeout=timeout if timeout is not None else account.timeout,
-    )
+1421
def resolve_account_spec(
+    account: AccountSpec,
+    *,
+    login: int | str | None = None,
+    password: str | None = None,
+    server: str | None = None,
+    path: str | None = None,
+    timeout: int | None = None,
+) -> AccountSpec:
+    """Resolve an account's credentials from overrides and ``${ENV_VAR}`` values.
+
+    Explicit override arguments take precedence over the corresponding
+    :class:`AccountSpec` fields. The resolved string fields (``login``,
+    ``password``, ``server``, ``path``) have any ``${ENV_VAR}`` placeholders
+    substituted from the environment.
+
+    Args:
+        account: Source account specification.
+        login: Optional explicit login override.
+        password: Optional explicit password override.
+        server: Optional explicit server override.
+        path: Optional explicit terminal path override.
+        timeout: Optional explicit connection timeout override.
+
+    Returns:
+        A new :class:`AccountSpec` with resolved credentials and the original
+        symbols preserved. Raises ``ValueError`` (via
+        :func:`substitute_env_placeholders`) if a referenced environment
+        variable is not set.
+    """
+    return AccountSpec(
+        symbols=account.symbols,
+        login=_resolve_login(login, account.login),
+        password=_resolve_field(password, account.password),
+        server=_resolve_field(server, account.server),
+        path=_resolve_field(path, account.path),
+        timeout=timeout if timeout is not None else account.timeout,
+    )
 
@@ -6876,7 +6876,11 @@ substituted from the environment.

Source code in mt5cli/sdk.py -
1428
+              
1424
+1425
+1426
+1427
+1428
 1429
 1430
 1431
@@ -6909,48 +6913,44 @@ substituted from the environment.

1458 1459 1460 -1461 -1462 -1463 -1464 -1465
def resolve_account_specs(
-    accounts: Sequence[AccountSpec],
-    *,
-    login: int | str | None = None,
-    password: str | None = None,
-    server: str | None = None,
-    path: str | None = None,
-    timeout: int | None = None,
-) -> list[AccountSpec]:
-    """Resolve credentials for multiple accounts.
-
-    Applies the same overrides and ``${ENV_VAR}`` substitution as
-    :func:`resolve_account_spec` to every account.
-
-    Args:
-        accounts: Source account specifications.
-        login: Optional explicit login override applied to each account.
-        password: Optional explicit password override applied to each account.
-        server: Optional explicit server override applied to each account.
-        path: Optional explicit terminal path override applied to each account.
-        timeout: Optional explicit timeout override applied to each account.
-
-    Returns:
-        Resolved account specifications in the original order. Raises
-        ``ValueError`` (via :func:`substitute_env_placeholders`) if a referenced
-        environment variable is not set.
-    """
-    return [
-        resolve_account_spec(
-            account,
-            login=login,
-            password=password,
-            server=server,
-            path=path,
-            timeout=timeout,
-        )
-        for account in accounts
-    ]
+1461
def resolve_account_specs(
+    accounts: Sequence[AccountSpec],
+    *,
+    login: int | str | None = None,
+    password: str | None = None,
+    server: str | None = None,
+    path: str | None = None,
+    timeout: int | None = None,
+) -> list[AccountSpec]:
+    """Resolve credentials for multiple accounts.
+
+    Applies the same overrides and ``${ENV_VAR}`` substitution as
+    :func:`resolve_account_spec` to every account.
+
+    Args:
+        accounts: Source account specifications.
+        login: Optional explicit login override applied to each account.
+        password: Optional explicit password override applied to each account.
+        server: Optional explicit server override applied to each account.
+        path: Optional explicit terminal path override applied to each account.
+        timeout: Optional explicit timeout override applied to each account.
+
+    Returns:
+        Resolved account specifications in the original order. Raises
+        ``ValueError`` (via :func:`substitute_env_placeholders`) if a referenced
+        environment variable is not set.
+    """
+    return [
+        resolve_account_spec(
+            account,
+            login=login,
+            password=password,
+            server=server,
+            path=path,
+            timeout=timeout,
+        )
+        for account in accounts
+    ]
 
@@ -7052,7 +7052,11 @@ substituted from the environment.

Source code in mt5cli/sdk.py -
1329
+              
1325
+1326
+1327
+1328
+1329
 1330
 1331
 1332
@@ -7071,34 +7075,30 @@ substituted from the environment.

1345 1346 1347 -1348 -1349 -1350 -1351 -1352
def substitute_env_placeholders(value: str) -> str:
-    """Replace ``${ENV_VAR}`` placeholders in a string with environment values.
-
-    Args:
-        value: String that may contain one or more ``${ENV_VAR}`` placeholders.
-
-    Returns:
-        The string with every placeholder replaced by its environment value.
-
-    Raises:
-        ValueError: If a referenced environment variable is not set.
-    """
-    parts: list[str] = []
-    last_end = 0
-    for match in _ENV_PLACEHOLDER_PATTERN.finditer(value):
-        parts.append(value[last_end : match.start()])
-        name = match.group("name")
-        if name not in os.environ:
-            msg = f"Environment variable {name!r} is not set."
-            raise ValueError(msg)
-        parts.append(os.environ[name])
-        last_end = match.end()
-    parts.append(value[last_end:])
-    return "".join(parts)
+1348
def substitute_env_placeholders(value: str) -> str:
+    """Replace ``${ENV_VAR}`` placeholders in a string with environment values.
+
+    Args:
+        value: String that may contain one or more ``${ENV_VAR}`` placeholders.
+
+    Returns:
+        The string with every placeholder replaced by its environment value.
+
+    Raises:
+        ValueError: If a referenced environment variable is not set.
+    """
+    parts: list[str] = []
+    last_end = 0
+    for match in _ENV_PLACEHOLDER_PATTERN.finditer(value):
+        parts.append(value[last_end : match.start()])
+        name = match.group("name")
+        if name not in os.environ:
+            msg = f"Environment variable {name!r} is not set."
+            raise ValueError(msg)
+        parts.append(os.environ[name])
+        last_end = match.end()
+    parts.append(value[last_end:])
+    return "".join(parts)
 
@@ -7125,19 +7125,19 @@ substituted from the environment.

Source code in mt5cli/sdk.py -
1801
+              
def symbol_info(
-    symbol: str,
-    *,
-    config: Mt5Config | None = None,
-) -> pd.DataFrame:
-    """Return details for one symbol."""
-    return _make_client(config=config).symbol_info(symbol)
+1803
def symbol_info(
+    symbol: str,
+    *,
+    config: Mt5Config | None = None,
+) -> pd.DataFrame:
+    """Return details for one symbol."""
+    return _make_client(config=config).symbol_info(symbol)
 
@@ -7164,19 +7164,19 @@ substituted from the environment.

Source code in mt5cli/sdk.py -
1909
+              
def symbol_info_tick(
-    symbol: str,
-    *,
-    config: Mt5Config | None = None,
-) -> pd.DataFrame:
-    """Return the last tick for a symbol."""
-    return _make_client(config=config).symbol_info_tick(symbol)
+1911
def symbol_info_tick(
+    symbol: str,
+    *,
+    config: Mt5Config | None = None,
+) -> pd.DataFrame:
+    """Return the last tick for a symbol."""
+    return _make_client(config=config).symbol_info_tick(symbol)
 
@@ -7205,19 +7205,19 @@ substituted from the environment.

Source code in mt5cli/sdk.py -
1792
+              
def symbols(
-    group: str | None = None,
-    *,
-    config: Mt5Config | None = None,
-) -> pd.DataFrame:
-    """Return the symbol list."""
-    return _make_client(config=config).symbols(group=group)
+1794
def symbols(
+    group: str | None = None,
+    *,
+    config: Mt5Config | None = None,
+) -> pd.DataFrame:
+    """Return the symbol list."""
+    return _make_client(config=config).symbols(group=group)
 
@@ -7244,11 +7244,11 @@ substituted from the environment.

Source code in mt5cli/sdk.py -
def terminal_info(*, config: Mt5Config | None = None) -> pd.DataFrame:
-    """Return terminal information."""
-    return _make_client(config=config).terminal_info()
+              
def terminal_info(*, config: Mt5Config | None = None) -> pd.DataFrame:
+    """Return terminal information."""
+    return _make_client(config=config).terminal_info()
 
@@ -7502,7 +7502,11 @@ timeframes when None).

Source code in mt5cli/sdk.py -
913
+              
909
+910
+911
+912
+913
 914
 915
 916
@@ -7571,84 +7575,80 @@ timeframes when None).

979 980 981 -982 -983 -984 -985 -986
def update_history(  # noqa: PLR0913
-    *,
-    client: Mt5DataClient,
-    output: Path | str,
-    symbols: Sequence[str],
-    datasets: set[Dataset] | None = None,
-    timeframes: Sequence[int | str] | None = None,
-    flags: int | str = "ALL",
-    lookback_hours: float = 24.0,
-    date_to: datetime | str | None = None,
-    deduplicate: bool = True,
-    create_rate_views: bool = True,
-    with_views: bool = False,
-    include_account_events: bool = True,
-) -> None:
-    """Incrementally append MT5 history into a SQLite database.
-
-    Uses an already-connected ``Mt5DataClient`` and does not create or close
-    the MT5 connection. For first-time tables, data is fetched from
-    ``date_to - lookback_hours``. Subsequent runs resume from existing
-    ``MAX(time)`` per symbol (and timeframe for rates); when
-    ``include_account_events=True``, account-level deals use a separate cursor
-    over ``type NOT IN (0, 1)`` / empty-symbol rows.
-
-    Args:
-        client: Connected MT5 data client.
-        output: SQLite database path.
-        symbols: Symbols to update.
-        datasets: Datasets to include (defaults to all).
-        timeframes: Rate timeframes to update (defaults to all fixed MT5
-            timeframes when None).
-        flags: Tick copy flags as integer or name (e.g. ``ALL``).
-        lookback_hours: First-run lookback when a table has no prior rows.
-        date_to: Optional update end datetime. Defaults to now (UTC).
-        deduplicate: Remove duplicate rows after append, keeping latest ROWID.
-        create_rate_views: Create ``rate_<symbol>__<timeframe>`` views.
-        with_views: Create ``cash_events`` and ``positions_reconstructed`` views.
-        include_account_events: Include account-level cash events in
-            ``history_deals`` when True.
-    """
-    request = _resolve_update_history_request(
-        output=output,
-        symbols=symbols,
-        datasets=datasets,
-        timeframes=timeframes,
-        flags=flags,
-        lookback_hours=lookback_hours,
-        date_to=date_to,
-    )
-    if request is None:
-        return
-    logger.info(
-        "Updating history in SQLite: symbols=%s, datasets=%s, path=%s",
-        list(symbols),
-        sorted(dataset.value for dataset in request.selected),
-        request.output_path,
-    )
-    with sqlite3.connect(request.output_path) as conn:
-        conn.execute("PRAGMA journal_mode=WAL")
-        conn.execute("PRAGMA synchronous=NORMAL")
-        write_incremental_datasets(
-            conn,
-            client,
-            symbols,
-            request.selected,
-            request.resolved_timeframes,
-            request.resolved_tick_flags,
-            request.fallback_start,
-            request.end,
-            deduplicate=deduplicate,
-            create_rate_views=create_rate_views,
-            with_views=with_views,
-            include_account_events=include_account_events,
-        )
+982
def update_history(  # noqa: PLR0913
+    *,
+    client: Mt5DataClient,
+    output: Path | str,
+    symbols: Sequence[str],
+    datasets: set[Dataset] | None = None,
+    timeframes: Sequence[int | str] | None = None,
+    flags: int | str = "ALL",
+    lookback_hours: float = 24.0,
+    date_to: datetime | str | None = None,
+    deduplicate: bool = True,
+    create_rate_views: bool = True,
+    with_views: bool = False,
+    include_account_events: bool = True,
+) -> None:
+    """Incrementally append MT5 history into a SQLite database.
+
+    Uses an already-connected ``Mt5DataClient`` and does not create or close
+    the MT5 connection. For first-time tables, data is fetched from
+    ``date_to - lookback_hours``. Subsequent runs resume from existing
+    ``MAX(time)`` per symbol (and timeframe for rates); when
+    ``include_account_events=True``, account-level deals use a separate cursor
+    over ``type NOT IN (0, 1)`` / empty-symbol rows.
+
+    Args:
+        client: Connected MT5 data client.
+        output: SQLite database path.
+        symbols: Symbols to update.
+        datasets: Datasets to include (defaults to all).
+        timeframes: Rate timeframes to update (defaults to all fixed MT5
+            timeframes when None).
+        flags: Tick copy flags as integer or name (e.g. ``ALL``).
+        lookback_hours: First-run lookback when a table has no prior rows.
+        date_to: Optional update end datetime. Defaults to now (UTC).
+        deduplicate: Remove duplicate rows after append, keeping latest ROWID.
+        create_rate_views: Create ``rate_<symbol>__<timeframe>`` views.
+        with_views: Create ``cash_events`` and ``positions_reconstructed`` views.
+        include_account_events: Include account-level cash events in
+            ``history_deals`` when True.
+    """
+    request = _resolve_update_history_request(
+        output=output,
+        symbols=symbols,
+        datasets=datasets,
+        timeframes=timeframes,
+        flags=flags,
+        lookback_hours=lookback_hours,
+        date_to=date_to,
+    )
+    if request is None:
+        return
+    logger.info(
+        "Updating history in SQLite: symbols=%s, datasets=%s, path=%s",
+        list(symbols),
+        sorted(dataset.value for dataset in request.selected),
+        request.output_path,
+    )
+    with sqlite3.connect(request.output_path) as conn:
+        conn.execute("PRAGMA journal_mode=WAL")
+        conn.execute("PRAGMA synchronous=NORMAL")
+        write_incremental_datasets(
+            conn,
+            client,
+            symbols,
+            request.selected,
+            request.resolved_timeframes,
+            request.resolved_tick_flags,
+            request.fallback_start,
+            request.end,
+            deduplicate=deduplicate,
+            create_rate_views=create_rate_views,
+            with_views=with_views,
+            include_account_events=include_account_events,
+        )
 
@@ -7688,7 +7688,11 @@ timeframes when None).

Source code in mt5cli/sdk.py -
 989
+              
 985
+ 986
+ 987
+ 988
+ 989
  990
  991
  992
@@ -7729,56 +7733,52 @@ timeframes when None).

1027 1028 1029 -1030 -1031 -1032 -1033 -1034
def update_history_with_config(  # noqa: PLR0913
-    *,
-    output: Path | str,
-    symbols: Sequence[str],
-    config: Mt5Config | None = None,
-    datasets: set[Dataset] | None = None,
-    timeframes: Sequence[int | str] | None = None,
-    flags: int | str = "ALL",
-    lookback_hours: float = 24.0,
-    date_to: datetime | str | None = None,
-    deduplicate: bool = True,
-    create_rate_views: bool = True,
-    with_views: bool = False,
-    include_account_events: bool = True,
-) -> None:
-    """Incrementally append MT5 history, opening and closing the MT5 connection.
-
-    Convenience wrapper around :func:`update_history` for standalone use.
-    """
-    request = _resolve_update_history_request(
-        output=output,
-        symbols=symbols,
-        datasets=datasets,
-        timeframes=timeframes,
-        flags=flags,
-        lookback_hours=lookback_hours,
-        date_to=date_to,
-    )
-    if request is None:
-        return
-    mt5_config = config or build_config()
-    with _connected_client(mt5_config) as client:
-        update_history(
-            client=client,
-            output=output,
-            symbols=symbols,
-            datasets=datasets,
-            timeframes=timeframes,
-            flags=flags,
-            lookback_hours=lookback_hours,
-            date_to=date_to,
-            deduplicate=deduplicate,
-            create_rate_views=create_rate_views,
-            with_views=with_views,
-            include_account_events=include_account_events,
-        )
+1030
def update_history_with_config(  # noqa: PLR0913
+    *,
+    output: Path | str,
+    symbols: Sequence[str],
+    config: Mt5Config | None = None,
+    datasets: set[Dataset] | None = None,
+    timeframes: Sequence[int | str] | None = None,
+    flags: int | str = "ALL",
+    lookback_hours: float = 24.0,
+    date_to: datetime | str | None = None,
+    deduplicate: bool = True,
+    create_rate_views: bool = True,
+    with_views: bool = False,
+    include_account_events: bool = True,
+) -> None:
+    """Incrementally append MT5 history, opening and closing the MT5 connection.
+
+    Convenience wrapper around :func:`update_history` for standalone use.
+    """
+    request = _resolve_update_history_request(
+        output=output,
+        symbols=symbols,
+        datasets=datasets,
+        timeframes=timeframes,
+        flags=flags,
+        lookback_hours=lookback_hours,
+        date_to=date_to,
+    )
+    if request is None:
+        return
+    mt5_config = config or build_config()
+    with _connected_client(mt5_config) as client:
+        update_history(
+            client=client,
+            output=output,
+            symbols=symbols,
+            datasets=datasets,
+            timeframes=timeframes,
+            flags=flags,
+            lookback_hours=lookback_hours,
+            date_to=date_to,
+            deduplicate=deduplicate,
+            create_rate_views=create_rate_views,
+            with_views=with_views,
+            include_account_events=include_account_events,
+        )
 
@@ -7803,11 +7803,11 @@ timeframes when None).

Source code in mt5cli/sdk.py -
def version(*, config: Mt5Config | None = None) -> pd.DataFrame:
-    """Return MetaTrader5 version information."""
-    return _make_client(config=config).version()
+              
def version(*, config: Mt5Config | None = None) -> pd.DataFrame:
+    """Return MetaTrader5 version information."""
+    return _make_client(config=config).version()
 
diff --git a/api/utils/index.html b/api/utils/index.html index cb4f7a3..11d07df 100644 --- a/api/utils/index.html +++ b/api/utils/index.html @@ -233,11 +233,7 @@ -
TICK_FLAG_MAP: dict[str, int] = {
-    "ALL": 1,
-    "INFO": 2,
-    "TRADE": 4,
-}
+
TICK_FLAG_MAP: dict[str, int] = dict(COPY_TICKS_MAP)
 
@@ -250,38 +246,20 @@ -

- TIMEFRAME_MAP +

+ TIMEFRAME_NAMES module-attribute -

-
TIMEFRAME_MAP: dict[str, int] = {
-    "M1": 1,
-    "M2": 2,
-    "M3": 3,
-    "M4": 4,
-    "M5": 5,
-    "M6": 6,
-    "M10": 10,
-    "M12": 12,
-    "M15": 15,
-    "M20": 20,
-    "M30": 30,
-    "H1": 16385,
-    "H2": 16386,
-    "H3": 16387,
-    "H4": 16388,
-    "H6": 16390,
-    "H8": 16392,
-    "H12": 16396,
-    "D1": 16408,
-    "W1": 32769,
-    "MN1": 49153,
-}
+
+
TIMEFRAME_NAMES: tuple[str, ...] = tuple(
+    name
+    for name in TIMEFRAME_MAP
+    if not startswith("TIMEFRAME_")
+)
 
@@ -989,57 +967,57 @@
Source code in mt5cli/utils.py -
237
+              
def detect_format(
-    output_path: Path,
-    explicit_format: str | None = None,
-) -> str:
-    """Detect the output format from a file extension or explicit format string.
-
-    Args:
-        output_path: Path to the output file.
-        explicit_format: Explicitly specified format, if any.
-
-    Returns:
-        The detected format string.
-
-    Raises:
-        ValueError: If the format cannot be determined.
-    """
-    if explicit_format is not None:
-        return explicit_format
-    suffix = output_path.suffix.lower()
-    if suffix in _FORMAT_EXTENSIONS:
-        return _FORMAT_EXTENSIONS[suffix]
-    msg = (
-        f"Cannot detect format from extension '{suffix}'."
-        " Use --format to specify the output format."
-    )
-    raise ValueError(msg)
+241
def detect_format(
+    output_path: Path,
+    explicit_format: str | None = None,
+) -> str:
+    """Detect the output format from a file extension or explicit format string.
+
+    Args:
+        output_path: Path to the output file.
+        explicit_format: Explicitly specified format, if any.
+
+    Returns:
+        The detected format string.
+
+    Raises:
+        ValueError: If the format cannot be determined.
+    """
+    if explicit_format is not None:
+        return explicit_format
+    suffix = output_path.suffix.lower()
+    if suffix in _FORMAT_EXTENSIONS:
+        return _FORMAT_EXTENSIONS[suffix]
+    msg = (
+        f"Cannot detect format from extension '{suffix}'."
+        " Use --format to specify the output format."
+    )
+    raise ValueError(msg)
 
@@ -1171,7 +1149,28 @@
Source code in mt5cli/utils.py -
309
+              
288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
 310
 311
 312
@@ -1188,66 +1187,45 @@
 323
 324
 325
-326
-327
-328
-329
-330
-331
-332
-333
-334
-335
-336
-337
-338
-339
-340
-341
-342
-343
-344
-345
-346
-347
def export_dataframe(
-    df: pd.DataFrame,
-    output_path: Path,
-    output_format: str,
-    table_name: str = "data",
-) -> None:
-    """Export a pandas DataFrame to the specified file format.
-
-    Args:
-        df: DataFrame to export.
-        output_path: Path to the output file.
-        output_format: Output format (csv, json, parquet, or sqlite3).
-        table_name: Table name for SQLite3 output.
-
-    Raises:
-        ValueError: If the output format is not supported.
-    """
-    if output_format == "csv":
-        df.to_csv(output_path, index=False)
-    elif output_format == "json":
-        df.to_json(
-            output_path,
-            orient="records",
-            date_format="iso",
-            indent=2,
-        )
-    elif output_format == "parquet":
-        df.to_parquet(output_path, index=False)
-    elif output_format == "sqlite3":
-        export_dataframe_to_sqlite(
-            df,
-            output_path,
-            table_name,
-            if_exists=IfExists.REPLACE,
-            index=False,
-        )
-    else:
-        msg = f"Unsupported output format: {output_format}"
-        raise ValueError(msg)
+326
def export_dataframe(
+    df: pd.DataFrame,
+    output_path: Path,
+    output_format: str,
+    table_name: str = "data",
+) -> None:
+    """Export a pandas DataFrame to the specified file format.
+
+    Args:
+        df: DataFrame to export.
+        output_path: Path to the output file.
+        output_format: Output format (csv, json, parquet, or sqlite3).
+        table_name: Table name for SQLite3 output.
+
+    Raises:
+        ValueError: If the output format is not supported.
+    """
+    if output_format == "csv":
+        df.to_csv(output_path, index=False)
+    elif output_format == "json":
+        df.to_json(
+            output_path,
+            orient="records",
+            date_format="iso",
+            indent=2,
+        )
+    elif output_format == "parquet":
+        df.to_parquet(output_path, index=False)
+    elif output_format == "sqlite3":
+        export_dataframe_to_sqlite(
+            df,
+            output_path,
+            table_name,
+            if_exists=IfExists.REPLACE,
+            index=False,
+        )
+    else:
+        msg = f"Unsupported output format: {output_format}"
+        raise ValueError(msg)
 
@@ -1423,7 +1401,28 @@ columns when appending frequently.

Source code in mt5cli/utils.py -
265
+              
244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
 266
 267
 268
@@ -1443,69 +1442,48 @@ columns when appending frequently.

282 283 284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299 -300 -301 -302 -303 -304 -305 -306
def export_dataframe_to_sqlite(
-    df: pd.DataFrame,
-    output_path: Path,
-    table_name: str = "data",
-    *,
-    if_exists: IfExists = IfExists.APPEND,
-    index: bool = False,
-    index_label: str | None = None,
-    deduplicate_on: Sequence[str] | None = None,
-) -> None:
-    """Write a DataFrame to SQLite with configurable append and deduplication.
-
-    Args:
-        df: DataFrame to export.
-        output_path: SQLite database path.
-        table_name: Target table name.
-        if_exists: Conflict behavior when the table already exists.
-        index: Whether to write the DataFrame index as a column.
-        index_label: Column name for the index when ``index=True``.
-        deduplicate_on: Optional key columns to deduplicate after writing,
-            keeping the latest ``ROWID`` per key group. Deduplication scans the
-            full table, so repeated appends cost O(table size); index the key
-            columns when appending frequently.
-    """
-    with sqlite3.connect(output_path) as conn:
-        df.to_sql(  # type: ignore[reportUnknownMemberType]
-            table_name,
-            conn,
-            if_exists=if_exists.value,
-            index=index,
-            index_label=index_label,
-        )
-        if deduplicate_on:
-            from .history import drop_duplicates_in_table  # noqa: PLC0415
-
-            drop_duplicates_in_table(
-                conn.cursor(),
-                table_name,
-                list(deduplicate_on),
-                keep="last",
-            )
-            conn.commit()
+285
def export_dataframe_to_sqlite(
+    df: pd.DataFrame,
+    output_path: Path,
+    table_name: str = "data",
+    *,
+    if_exists: IfExists = IfExists.APPEND,
+    index: bool = False,
+    index_label: str | None = None,
+    deduplicate_on: Sequence[str] | None = None,
+) -> None:
+    """Write a DataFrame to SQLite with configurable append and deduplication.
+
+    Args:
+        df: DataFrame to export.
+        output_path: SQLite database path.
+        table_name: Target table name.
+        if_exists: Conflict behavior when the table already exists.
+        index: Whether to write the DataFrame index as a column.
+        index_label: Column name for the index when ``index=True``.
+        deduplicate_on: Optional key columns to deduplicate after writing,
+            keeping the latest ``ROWID`` per key group. Deduplication scans the
+            full table, so repeated appends cost O(table size); index the key
+            columns when appending frequently.
+    """
+    with sqlite3.connect(output_path) as conn:
+        df.to_sql(  # type: ignore[reportUnknownMemberType]
+            table_name,
+            conn,
+            if_exists=if_exists.value,
+            index=index,
+            index_label=index_label,
+        )
+        if deduplicate_on:
+            from .history import drop_duplicates_in_table  # noqa: PLC0415
+
+            drop_duplicates_in_table(
+                conn.cursor(),
+                table_name,
+                list(deduplicate_on),
+                keep="last",
+            )
+            conn.commit()
 
@@ -1608,47 +1586,47 @@ columns when appending frequently.

Source code in mt5cli/utils.py -
def parse_datetime(value: str) -> datetime:
-    """Parse an ISO 8601 datetime string to a timezone-aware datetime.
-
-    Args:
-        value: ISO 8601 datetime string (e.g., '2024-01-01' or
-            '2024-01-01T12:00:00+00:00').
-
-    Returns:
-        Parsed datetime with UTC timezone if no timezone is specified.
-
-    Raises:
-        ValueError: If the string cannot be parsed.
-    """
-    try:
-        dt = datetime.fromisoformat(value)
-    except ValueError:
-        msg = f"Invalid datetime format: '{value}'. Use ISO 8601 format."
-        raise ValueError(msg) from None
-    if dt.tzinfo is None:
-        dt = dt.replace(tzinfo=UTC)
-    return dt
+              
def parse_datetime(value: str) -> datetime:
+    """Parse an ISO 8601 datetime string to a timezone-aware datetime.
+
+    Args:
+        value: ISO 8601 datetime string (e.g., '2024-01-01' or
+            '2024-01-01T12:00:00+00:00').
+
+    Returns:
+        Parsed datetime with UTC timezone if no timezone is specified.
+
+    Raises:
+        ValueError: If the string cannot be parsed.
+    """
+    try:
+        dt = datetime.fromisoformat(value)
+    except ValueError:
+        msg = f"Invalid datetime format: '{value}'. Use ISO 8601 format."
+        raise ValueError(msg) from None
+    if dt.tzinfo is None:
+        dt = dt.replace(tzinfo=UTC)
+    return dt
 
@@ -1751,7 +1729,26 @@ JSON object.

Source code in mt5cli/utils.py -
@@ -1913,47 +1891,49 @@ JSON object.

Source code in mt5cli/utils.py -
423
+              
404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
 424
 425
 426
@@ -1762,56 +1759,37 @@ JSON object.

431 432 433 -434 -435 -436 -437 -438 -439 -440 -441 -442 -443 -444 -445 -446 -447 -448 -449 -450 -451 -452 -453
def parse_request(value: str) -> dict[str, Any]:
-    """Parse a JSON-formatted order request string or file reference.
-
-    Args:
-        value: JSON object string, or '@path' to read JSON from a file.
-
-    Returns:
-        Parsed request dictionary.
-
-    Raises:
-        ValueError: If the request file cannot be read or the value is not a
-            JSON object.
-    """
-    if value.startswith("@"):
-        path = Path(value[1:])
-        try:
-            text = path.read_text(encoding="utf-8")
-        except (OSError, UnicodeDecodeError) as exc:
-            msg = f"Failed to read JSON request file '{path}': {exc}"
-            raise ValueError(msg) from exc
-    else:
-        text = value
-    try:
-        parsed: object = json.loads(text)
-    except json.JSONDecodeError as exc:
-        msg = f"Invalid JSON request: {exc}"
-        raise ValueError(msg) from exc
-    if not _is_request_dict(parsed):
-        msg = "Order request must be a JSON object."
-        raise ValueError(msg)
-    return parsed
+434
def parse_request(value: str) -> dict[str, Any]:
+    """Parse a JSON-formatted order request string or file reference.
+
+    Args:
+        value: JSON object string, or '@path' to read JSON from a file.
+
+    Returns:
+        Parsed request dictionary.
+
+    Raises:
+        ValueError: If the request file cannot be read or the value is not a
+            JSON object.
+    """
+    if value.startswith("@"):
+        path = Path(value[1:])
+        try:
+            text = path.read_text(encoding="utf-8")
+        except (OSError, UnicodeDecodeError) as exc:
+            msg = f"Failed to read JSON request file '{path}': {exc}"
+            raise ValueError(msg) from exc
+    else:
+        text = value
+    try:
+        parsed: object = json.loads(text)
+    except json.JSONDecodeError as exc:
+        msg = f"Invalid JSON request: {exc}"
+        raise ValueError(msg) from exc
+    if not _is_request_dict(parsed):
+        msg = "Order request must be a JSON object."
+        raise ValueError(msg)
+    return parsed
 
@@ -1826,7 +1804,7 @@ JSON object.

-
parse_tick_flags(value: str) -> int
+
parse_tick_flags(value: object) -> int
 
@@ -1850,11 +1828,11 @@ JSON object.

value
- str + object
-

Tick flag name (ALL, INFO, TRADE) or integer value.

+

Tick flag name (ALL, INFO, TRADE, COPY_TICKS_*) or integer value.

@@ -1880,7 +1858,7 @@ JSON object.

-

Integer tick flag value.

+

Integer tick flag value compatible with MetaTrader 5 COPY_TICKS_*.

def parse_tick_flags(value: str) -> int:
-    """Parse tick flags string or integer value.
-
-    Args:
-        value: Tick flag name (ALL, INFO, TRADE) or integer value.
-
-    Returns:
-        Integer tick flag value.
-
-    Raises:
-        ValueError: If the flag is invalid.
-    """
-    upper = value.upper()
-    if upper in TICK_FLAG_MAP:
-        return TICK_FLAG_MAP[upper]
-    try:
-        return int(value)
-    except ValueError:
-        valid = ", ".join(TICK_FLAG_MAP)
-        msg = f"Invalid tick flags: '{value}'. Use one of: {valid}, or an integer."
-        raise ValueError(msg) from None
+              
def parse_tick_flags(value: object) -> int:
+    """Parse tick flags string or integer value.
+
+    Args:
+        value: Tick flag name (ALL, INFO, TRADE, COPY_TICKS_*) or integer value.
+
+    Returns:
+        Integer tick flag value compatible with MetaTrader 5 ``COPY_TICKS_*``.
+
+    Raises:
+        ValueError: If the flag is invalid.
+    """
+    try:
+        return _parse_copy_ticks(value)
+    except ValueError:
+        display = value if isinstance(value, str) else repr(value)
+        valid = ", ".join(_TICK_FLAG_NAMES)
+        msg = (
+            f"Invalid tick flags: '{display}'. "
+            f"Use one of: {valid}, or a supported integer."
+        )
+        raise ValueError(msg) from None
 
@@ -1968,7 +1948,7 @@ JSON object.

-
parse_timeframe(value: str) -> int
+
parse_timeframe(value: object) -> int
 
@@ -1992,7 +1972,7 @@ JSON object.

value
- str + object
@@ -2055,47 +2035,49 @@ JSON object.

Source code in mt5cli/utils.py -
def parse_timeframe(value: str) -> int:
-    """Parse a timeframe string or integer value.
-
-    Args:
-        value: Timeframe name (e.g., 'M1', 'H1', 'D1') or integer value.
-
-    Returns:
-        Integer timeframe value.
-
-    Raises:
-        ValueError: If the timeframe is invalid.
-    """
-    upper = value.upper()
-    if upper in TIMEFRAME_MAP:
-        return TIMEFRAME_MAP[upper]
-    try:
-        return int(value)
-    except ValueError:
-        valid = ", ".join(TIMEFRAME_MAP)
-        msg = f"Invalid timeframe: '{value}'. Use one of: {valid}, or an integer."
-        raise ValueError(msg) from None
+              
def parse_timeframe(value: object) -> int:
+    """Parse a timeframe string or integer value.
+
+    Args:
+        value: Timeframe name (e.g., 'M1', 'H1', 'D1') or integer value.
+
+    Returns:
+        Integer timeframe value.
+
+    Raises:
+        ValueError: If the timeframe is invalid.
+    """
+    try:
+        return _parse_timeframe(value)
+    except ValueError:
+        display = value if isinstance(value, str) else repr(value)
+        valid = ", ".join(TIMEFRAME_NAMES)
+        msg = (
+            f"Invalid timeframe: '{display}'. "
+            f"Use one of: {valid}, or a supported integer."
+        )
+        raise ValueError(msg) from None
 
diff --git a/index.html b/index.html index 70fc528..d31dab2 100644 --- a/index.html +++ b/index.html @@ -113,6 +113,10 @@ +