# Converters

## mt5cli.converters

Shared conversion helpers for MT5 symbols, timeframes, and date ranges.

### __all__ `module-attribute`

    __all__ = [
        "ensure_utc",
        "granularity_name",
        "normalize_symbol",
        "normalize_symbols",
        "parse_date_range",
        "parse_datetime",
        "parse_tick_flags",
        "parse_timeframe",
        "recent_window",
    ]

### ensure_utc

    ensure_utc(value: datetime | str) -> datetime

Return a timezone-aware UTC datetime.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`value` | `datetime | str` | Datetime instance or ISO 8601 string. | required

Returns:

Type | Description
--- | ---
`datetime` | UTC-aware datetime.

Source code in `mt5cli/converters.py`

    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82

    def ensure_utc(value: datetime | str) -> datetime:
        """Return a timezone-aware UTC datetime.

        Args:
            value: Datetime instance or ISO 8601 string.

        Returns:
            UTC-aware datetime.
        """
        if isinstance(value, str):
            return parse_datetime(value)
        if value.tzinfo is None:
            return value.replace(tzinfo=UTC)
        return value.astimezone(UTC)

### granularity_name

    granularity_name(timeframe: int | str) -> str

Return a short granularity label for a timeframe integer or name.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`timeframe` | `int | str` | MT5 timeframe as integer or name (for example `M1`). | required

Returns:

Type | Description
--- | ---
`str` | Short name such as `M1` or the stringified integer when unknown.

Source code in `mt5cli/converters.py`

    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162

    def granularity_name(timeframe: int | str) -> str:
        """Return a short granularity label for a timeframe integer or name.

        Args:
            timeframe: MT5 timeframe as integer or name (for example ``M1``).

        Returns:
            Short name such as ``M1`` or the stringified integer when unknown.
        """
        tf = parse_timeframe(timeframe)
        try:
            name = _get_timeframe_name(tf)
        except ValueError:
            return str(tf)
        return name.removeprefix("TIMEFRAME_")

### normalize_symbol

    normalize_symbol(symbol: str) -> str

Normalize a broker symbol name for MT5 API calls.

Strips surrounding whitespace while preserving broker-specific casing and suffixes (for example `XAUUSDm`, `US500.cash`, or `EURUSD.r`).

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`symbol` | `str` | Raw symbol name. | required

Returns:

Type | Description
--- | ---
`str` | Normalized symbol string.

Raises:

Type | Description
--- | ---
`ValueError` | If the symbol is empty after normalization.

Source code in `mt5cli/converters.py`

    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47

    def normalize_symbol(symbol: str) -> str:
        """Normalize a broker symbol name for MT5 API calls.

        Strips surrounding whitespace while preserving broker-specific casing and
        suffixes (for example ``XAUUSDm``, ``US500.cash``, or ``EURUSD.r``).

        Args:
            symbol: Raw symbol name.

        Returns:
            Normalized symbol string.

        Raises:
            ValueError: If the symbol is empty after normalization.
        """
        normalized = symbol.strip()
        if not normalized:
            msg = "Symbol must not be empty."
            raise ValueError(msg)
        return normalized

### normalize_symbols

    normalize_symbols(symbols: Sequence[str]) -> list[str]

Normalize a sequence of broker symbol names.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`symbols` | `Sequence[str]` | Raw symbol names. | required

Returns:

Type | Description
--- | ---
`list[str]` | List of normalized, de-duplicated symbols preserving first-seen order.

Source code in `mt5cli/converters.py`

    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66

    def normalize_symbols(symbols: Sequence[str]) -> list[str]:
        """Normalize a sequence of broker symbol names.

        Args:
            symbols: Raw symbol names.

        Returns:
            List of normalized, de-duplicated symbols preserving first-seen order.
        """
        seen: set[str] = set()
        resolved: list[str] = []
        for symbol in symbols:
            normalized = normalize_symbol(symbol)
            if normalized not in seen:
                seen.add(normalized)
                resolved.append(normalized)
        return resolved

### parse_date_range

    parse_date_range(
        date_from: datetime | str, date_to: datetime | str
    ) -> tuple[datetime, datetime]

Parse and validate an inclusive UTC date range.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`date_from` | `datetime | str` | Range start as datetime or ISO 8601 string. | required
`date_to` | `datetime | str` | Range end as datetime or ISO 8601 string. | required

Returns:

Type | Description
--- | ---
`tuple[datetime, datetime]` | Tuple of UTC-aware `(start, end)` datetimes.

Raises:

Type | Description
--- | ---
`ValueError` | If `date_from` is after `date_to`.

Source code in `mt5cli/converters.py`

     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109

    def parse_date_range(
        date_from: datetime | str,
        date_to: datetime | str,
    ) -> tuple[datetime, datetime]:
        """Parse and validate an inclusive UTC date range.

        Args:
            date_from: Range start as datetime or ISO 8601 string.
            date_to: Range end as datetime or ISO 8601 string.

        Returns:
            Tuple of UTC-aware ``(start, end)`` datetimes.

        Raises:
            ValueError: If ``date_from`` is after ``date_to``.
        """
        start = ensure_utc(date_from)
        end = ensure_utc(date_to)
        if start > end:
            msg = (
                f"date_from ({start.isoformat()}) must not be after "
                f"date_to ({end.isoformat()})."
            )
            raise ValueError(msg)
        return start, end

### parse_datetime

    parse_datetime(value: str) -> datetime

Parse an ISO 8601 datetime string to a timezone-aware datetime.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`value` | `str` | ISO 8601 datetime string (e.g., '2024-01-01' or '2024-01-01T12:00:00+00:00'). | required

Returns:

Type | Description
--- | ---
`datetime` | Parsed datetime with UTC timezone if no timezone is specified.

Raises:

Type | Description
--- | ---
`ValueError` | If the string cannot be parsed.

Source code in `mt5cli/utils.py`

    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370

    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

### parse_tick_flags

    parse_tick_flags(value: object) -> int

Parse tick flags string or integer value.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`value` | `object` | Tick flag name (ALL, INFO, TRADE, COPY_TICKS_*) or integer value. | required

Returns:

Type | Description
--- | ---
`int` | Integer tick flag value compatible with MetaTrader 5 `COPY_TICKS_*`.

Raises:

Type | Description
--- | ---
`ValueError` | If the flag is invalid.

Source code in `mt5cli/utils.py`

    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418

    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

### parse_timeframe

    parse_timeframe(value: object) -> int

Parse a timeframe string or integer value.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`value` | `object` | Timeframe name (e.g., 'M1', 'H1', 'D1') or integer value. | required

Returns:

Type | Description
--- | ---
`int` | Integer timeframe value.

Raises:

Type | Description
--- | ---
`ValueError` | If the timeframe is invalid.

Source code in `mt5cli/utils.py`

    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394

    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

### recent_window

    recent_window(
        *,
        hours: float | None = None,
        seconds: float | None = None,
        date_to: datetime | str | None = None,
    ) -> tuple[datetime, datetime]

Build a trailing UTC window ending at `date_to` or now.

Exactly one of `hours` or `seconds` must be provided.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`hours` | `float | None` | Trailing window length in hours. | `None`
`seconds` | `float | None` | Trailing window length in seconds. | `None`
`date_to` | `datetime | str | None` | Window end. Defaults to current UTC time. | `None`

Returns:

Type | Description
--- | ---
`tuple[datetime, datetime]` | Tuple of UTC-aware `(start, end)` datetimes.

Raises:

Type | Description
--- | ---
`ValueError` | If neither or both window lengths are provided, or if a length is not positive.

Source code in `mt5cli/converters.py`

    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145

    def recent_window(
        *,
        hours: float | None = None,
        seconds: float | None = None,
        date_to: datetime | str | None = None,
    ) -> tuple[datetime, datetime]:
        """Build a trailing UTC window ending at ``date_to`` or now.

        Exactly one of ``hours`` or ``seconds`` must be provided.

        Args:
            hours: Trailing window length in hours.
            seconds: Trailing window length in seconds.
            date_to: Window end. Defaults to current UTC time.

        Returns:
            Tuple of UTC-aware ``(start, end)`` datetimes.

        Raises:
            ValueError: If neither or both window lengths are provided, or if a
                length is not positive.
        """
        if (hours is None) == (seconds is None):
            msg = "Provide exactly one of hours or seconds."
            raise ValueError(msg)
        if hours is not None:
            length = timedelta(hours=hours)
        else:
            length = timedelta(seconds=seconds if seconds is not None else 0)
        if length.total_seconds() <= 0:
            msg = "Window length must be positive."
            raise ValueError(msg)
        end = ensure_utc(date_to) if date_to is not None else datetime.now(UTC)
        return end - length, end

#### Search

From here you can search these documents. Enter your search terms below.

#### Keyboard Shortcuts

Keys | Action
--- | ---
`?` | Open this help
`n` | Next page
`p` | Previous page
`s` | Search