78c49238cf
* feat: add stable MT5Client public API and infrastructure layer Introduce a reusable public API for downstream trading applications: - MT5Client as the primary client abstraction with order_check/order_send - schemas module with DataKind contracts, validation, and normalization - converters, exceptions, retry, and storage facade modules - CLI order commands now route through MT5Client - connected_client made public; retry logic centralized - Contract tests for API surface, schemas, and storage round-trips - README and docs updated with Python API usage examples Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com> * fix: correct time coercion, broker-safe symbols, and execution docs - Normalize MT5 time columns with correct second/millisecond units - Coerce all present known MT5 time fields, including optional order times - Preserve broker symbol casing in normalize_symbol() - Document order_send() as a live execution primitive with clear scope boundaries - Add contract tests for timestamp and symbol normalization behavior Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
65 lines
1.7 KiB
Python
65 lines
1.7 KiB
Python
"""Retry and reconnect helpers for transient MT5 failures."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import time
|
|
from typing import TYPE_CHECKING, TypeVar
|
|
|
|
from .exceptions import is_recoverable_mt5_error
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Callable
|
|
|
|
T = TypeVar("T")
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
__all__ = [
|
|
"retry_with_backoff",
|
|
]
|
|
|
|
|
|
def retry_with_backoff(
|
|
fn: Callable[[], T],
|
|
*,
|
|
retry_count: int = 0,
|
|
backoff_base: float = 2.0,
|
|
operation: str = "MT5 operation",
|
|
) -> T:
|
|
"""Call ``fn`` with bounded exponential backoff on recoverable MT5 errors.
|
|
|
|
Only ``pdmt5.Mt5RuntimeError`` and ``pdmt5.Mt5TradingError`` are retried.
|
|
Other exceptions propagate immediately. The final failure is re-raised once
|
|
retries are exhausted.
|
|
|
|
Args:
|
|
fn: Callable performing MT5 work.
|
|
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.
|
|
operation: Label used in warning logs.
|
|
|
|
Returns:
|
|
Value returned by ``fn`` on success.
|
|
"""
|
|
attempts = max(retry_count, 0) + 1
|
|
for attempt in range(attempts - 1):
|
|
try:
|
|
return fn()
|
|
except Exception as exc:
|
|
if not is_recoverable_mt5_error(exc):
|
|
raise
|
|
delay = backoff_base ** (attempt + 1)
|
|
logger.warning(
|
|
"%s failed (attempt %d/%d): %s; retrying in %.1fs",
|
|
operation,
|
|
attempt + 1,
|
|
attempts,
|
|
exc,
|
|
delay,
|
|
)
|
|
time.sleep(delay)
|
|
return fn()
|