Quick Start

This guide makes a read-only connection. It does not place, modify, or cancel an order. Use an authorized demo account until you have validated the exact server build and every workflow your application needs.

1. Configure credentials

Set environment variables in your shell or secret manager:

export PYMT5_SERVER="wss://web.metatrader.app/terminal"
export PYMT5_LOGIN="your-demo-login"
export PYMT5_PASSWORD="your-demo-password"

Do not hard-code credentials, commit .env, or paste tokens and session data into an issue. The client does not implicitly load a .env file. Loading that file is the application’s responsibility.

2. Connect and load data

import asyncio
import os

from pymt5 import MT5WebClient


async def main() -> None:
    client = MT5WebClient(
        uri=os.environ["PYMT5_SERVER"],
        auto_reconnect=True,
    )
    async with client:
        await client.login(
            login=int(os.environ["PYMT5_LOGIN"]),
            password=os.environ["PYMT5_PASSWORD"],
        )

        # load_symbols() returns a name-keyed dict[str, SymbolInfo].
        symbols = await client.load_symbols()
        if not symbols:
            raise RuntimeError("The server returned no symbols")

        account = await client.get_account()
        print(f"Loaded {len(symbols)} symbols")
        print(f"Account currency: {account.get('currency', 'unknown')}")


asyncio.run(main())

The async context manager closes the transport on both success and failure. Calling login() again on an active session raises SessionError; call logout() before switching accounts.

3. Retrieve historical bars

After loading symbols, request a bounded historical range:

from datetime import datetime, timedelta, timezone

end = datetime.now(timezone.utc)
start = end - timedelta(days=7)
bars = await client.get_rates("EURUSD", 60, start, end)
print(f"Received {len(bars)} H1 bars")

Replace EURUSD with a symbol returned by your server. Historical responses are validated before parsing; an unknown or ambiguous layout raises ProtocolError rather than being guessed. See Market Data.

4. Observe a tick stream

Register the callback before subscribing. Choose a liquid symbol that is in an active trading session:

import os

symbol_name = os.environ.get("PYMT5_TEST_SYMBOL", "EURUSD")
info = symbols.get(symbol_name)
if info is None:
    raise ValueError(f"Symbol not available on this server: {symbol_name}")

def on_ticks(ticks):
    for tick in ticks:
        print(tick["symbol"], tick.get("bid"), tick.get("ask"))

client.on_tick(on_ticks)
subscription = await client.subscribe_ticks_managed([info.symbol_id])
async with subscription:
    await asyncio.sleep(30)

No tick during a closed market is not a protocol success or failure by itself. The subscription handle attempts cleanup on exit; a server rejection is surfaced and must not be described as a successful unsubscribe. Tick price scaling is server/build-sensitive; review Configuration before using price values.

5. Add explicit error handling

from pymt5 import (
    AuthenticationError,
    MT5ConnectionError,
    MT5TimeoutError,
    ProtocolError,
)

try:
    await client.ping()
except MT5TimeoutError:
    # The previous connection is isolated after an uncertain sent request.
    # Do not reuse it or assume what the server processed.
    print("Ping timed out; wait for a fresh connection")
except (MT5ConnectionError, ProtocolError) as exc:
    print(f"Transport or protocol failed: {exc}")

For login failures, catch AuthenticationError separately. For trading, a timeout leaves server execution unknown: reconcile orders, deals, and positions before any retry. See Reliability and Security and Operational Safety.

Next steps

  • Examples — runnable examples and their read-only/mutating scope

  • Configuration — compatibility modes and connection settings

  • Market Data — ticks, bars, symbol metadata, and order book

  • Troubleshooting — common connection and protocol failures