Skip to main content

pymt5

CI Python License: MIT Documentation Platform

Python client for the MT5 Web Terminal via reverse-engineered WebSocket binary protocol.

See the changelog for releases and subsequent changes.

What's new in 1.0.4

  • Trade requests retain the legacy wire format by default. An explicit build_6090 profile adds the reported reserved bytes and market-deal action.
  • Ambiguous or mismatched historical rate response lengths raise ProtocolError; choose standard or extended explicitly when the server's format is known.
  • Login rejects nonzero server response codes, and logout clears stored reconnect credentials after success. Switching accounts requires an explicit logout.
  • close_position() rejects invalid IDs and refuses automatic direction detection when the position or its matching symbol cannot be verified.

These changes have offline test coverage. The new build 6090 profile has not been independently verified against a broker in this release.

Requirements

  • Python 3.11+ (3.11, 3.12, 3.13)
  • Platforms: Linux, macOS, Windows
  • Dependencies: websockets, cryptography

Features

Core Protocol

  • WebSocket transport to wss://web.metatrader.app/terminal
  • AES-CBC encryption with zero IV and PKCS7 padding
  • Bootstrap handshake (cmd=0) with automatic key exchange
  • Login (cmd=28) with UTF-16LE encoded fields
  • ping, logout, change_password, trader_params
  • verify_code (cmd=27) — two-factor authentication
  • request_opening_verification / submit_opening_verification (cmd=27/40)
  • open_demo_account (cmd=30) and open_real_account (cmd=39)
  • enable_otp / disable_otp (cmd=43)
  • open_demo (cmd=30) — legacy init-style wrapper kept for compatibility
  • send_notification (cmd=42) — server notifications

Account Data

  • get_account (cmd=3) — account metadata and balance state: balance, credit, currency, leverage, account/server/company names, timezone, rights flags, trade settings, leverage rules, commission tables
  • get_account_summary() — AccountInfo dataclass (uses cmd=3 + positions)
  • get_positions_and_orders (cmd=4) — open positions and pending orders
  • get_positions() — convenience: open positions only
  • get_orders() — convenience: pending orders only
  • get_trade_history (cmd=5) — closed deals and historical orders
  • get_deals() — convenience: closed deals only

Market Data

  • get_symbols (cmd=34, zlib compressed) — full symbol list with metadata
  • get_symbol_groups (cmd=9) — category tree (Forex, Crypto, Indexes, etc.)
  • get_full_symbol_info (cmd=18) — detailed contract specs (tick size, margins, volumes, bond face value/accrued interest, trade schedule, subscription flags)
  • get_spreads (cmd=20) — spread data with proper schema parsing
  • get_rates (cmd=11) — historical OHLCV bars (M1 to MN1), with standard and extended formats
  • subscribe_ticks (cmd=7) — real-time tick push (cmd=8) with callback
  • subscribe_symbols — name-based tick subscription (resolves names via symbol cache)
  • subscribe_book (cmd=22) — order book / depth-of-market subscription
  • subscribe_book_by_name — name-based order book subscription

By default, rate responses are decoded automatically only when their length uniquely identifies the 48-byte standard or 56-byte extended format. Lengths divisible by both, including any nonzero multiple of 336 bytes, or by neither, raise ProtocolError rather than guessing. This is a behavior change for ambiguous or mismatched responses. Select the format confirmed for your server with MT5WebClient(rate_bar_format="standard") or "extended"; alternatively, pass bar_format="standard" or "extended" to get_rates() or the copy_rates_*() wrappers. The extended format includes real_volume. get_rates_raw() can help inspect an unknown response, but length alone does not identify an ambiguous format. Both get_rates() and get_rates_raw() raise ProtocolError for nonzero server response codes. Length validation cannot detect truncation or an incorrect format selection when the resulting size happens to match the selected record size.

Official MetaTrader5-style Helpers

  • Session/account: initialize, shutdown, account_info, terminal_info, version
  • Compatibility state: last_error
  • Symbols/history: symbols_total, symbols_get, symbol_info, positions_total, positions_get, orders_total, orders_get, history_orders_*, history_deals_*
  • Bars/trading: copy_rates_range, copy_rates_from, copy_rates_from_pos, copy_ticks_from, copy_ticks_range, order_send, order_check
  • Local formula helpers: order_calc_profit, order_calc_margin (best-effort for forex/CFD/futures/stock/bond modes that the current Web Terminal computes client-side)
  • DOM/cache helpers: market_book_add, market_book_get, market_book_release, symbol_select, symbol_info_tick
  • terminal_info and version are best-effort Web Terminal views derived from cmd=3 plus observed public build metadata
  • copy_ticks_* are cached views over observed cmd=8 pushes, and order_check is a local compatibility-layer pre-flight rather than a server RPC

Trading

  • Raw: trade_request — full control over all trade fields, returns TradeResult
  • TradeResult includes: deal, order, volume, price, bid, ask, comment, request_id
  • High-level helpers (auto-resolve digits from symbol cache):
    • buy_market / sell_market — market orders with optional SL/TP
    • buy_limit / sell_limit — limit pending orders
    • buy_stop / sell_stop — stop pending orders
    • buy_stop_limit / sell_stop_limit — stop-limit pending orders
    • close_position — close by opposite market order (auto-detects BUY/SELL direction)
    • close_position_by — close by opposite position (hedge netting)
    • modify_position_sltp — modify SL/TP of open position
    • modify_pending_order — modify pending order price/SL/TP
    • cancel_pending_order — remove a pending order

Symbol Cache & Info

  • load_symbols() — fetch and cache all symbols for fast name→id/digits lookup
  • get_symbol_info(name) — lookup SymbolInfo by name
  • get_symbol_id(name) — lookup symbol ID by name
  • symbol_names — list all cached symbol names

Miscellaneous Commands

  • get_corporate_links (cmd=44) — broker links (support, education, social)
  • send_raw_command — send any command with raw payload
  • send_bootstrap_command_52 — experimental wrapper for reserved bootstrap-only cmd=52

Push Notifications

  • on_tick(callback) — real-time tick pushes (cmd=8)
  • on_position_update(callback) — position change pushes
  • on_order_update(callback) — order change pushes
  • on_trade_update(callback) — combined position+order push in single callback
  • on_trade_transaction(callback) — order add/update/delete + balance updates (cmd=10)
  • on_symbol_update(callback) — symbol change pushes (cmd=13)
  • on_account_update(callback) — account balance/equity/margin pushes (cmd=14)
  • on_login_status(callback) — login status pushes (cmd=15)
  • on_symbol_details(callback) — extended quote data with options greeks (cmd=17)
  • on_trade_result(callback) — async trade execution results (cmd=19)
  • on_book_update(callback) — order book / DOM pushes (cmd=23)

Reliability

  • Async context manager (async with MT5WebClient() as client:)
  • Auto heartbeat — periodic ping after login (configurable interval)
  • Auto reconnect — optional reconnect on disconnect with exponential backoff
  • Disconnect callback — on_disconnect() for custom handling
  • Python logging — structured logging via pymt5.client and pymt5.transport loggers

Constants & Enums

  • TradeResult dataclass with retcode, description, success, deal, order, volume, price fields
  • 33 MT5 return codes with human-readable descriptions
  • Complete trade constants: TRADE_ACTION_* (including CLOSE_BY), ORDER_TYPE_* (including STOP_LIMIT), ORDER_FILLING_*, ORDER_TIME_*
  • Position/deal enums: POSITION_TYPE_*, DEAL_TYPE_* (all 15 types), DEAL_ENTRY_*
  • Order states: ORDER_STATE_* (all 7 states)
  • Symbol trade modes: SYMBOL_TRADE_MODE_*
  • Command IDs exported: CMD_GET_ACCOUNT, CMD_GET_SYMBOL_GROUPS, CMD_TRADE_UPDATE_PUSH, CMD_ACCOUNT_UPDATE_PUSH, CMD_SYMBOL_DETAILS_PUSH, CMD_TRADE_RESULT_PUSH, CMD_SUBSCRIBE_BOOK, CMD_BOOK_PUSH, CMD_GET_CORPORATE_LINKS

Tests

  • Offline unit tests cover protocol encoding, trade and rate parsing, session handling, reconnect logic, compatibility helpers, and push handlers.

Earlier functionality was tested against MetaQuotes-Demo on 2026-03-16 with Web Terminal build 5687. Those historical results do not verify the changes in 1.0.4.

Install

pip install mt5-web-client

Or install from source:

git clone https://github.com/cloudQuant/pymt5.git
cd pymt5
pip install -e .

For development (tests + type checking):

pip install -e ".[dev]"
make check

Quick Start

import asyncio
from pymt5 import MT5WebClient


async def main():
    async with MT5WebClient(auto_reconnect=True) as client:
        await client.login(login=12345678, password="your-password")

        # Load symbol cache
        await client.load_symbols()
        print(f"Loaded {len(client.symbol_names)} symbols")

        # Full account info (balance, equity, margin, leverage)
        acct = await client.get_account()
        print(f"Balance: {acct['balance']}, Currency: {acct['currency']}, Leverage: 1:{acct['leverage']}")

        # Symbol groups
        groups = await client.get_symbol_groups()
        print(f"Groups: {groups}")

        # Subscribe to ticks
        def on_ticks(ticks):
            for t in ticks:
                print(f"TICK {t.get('symbol', t['symbol_id'])}: bid={t['bid']} ask={t['ask']}")

        client.on_tick(on_ticks)
        await client.subscribe_symbols(["EURUSD", "GBPUSD"])

        # Register for all push notifications
        client.on_trade_update(lambda d: print(f"Positions: {len(d['positions'])}"))
        client.on_account_update(lambda d: print(f"Balance: {d.get('balance')}"))
        client.on_trade_transaction(lambda d: print(f"Transaction: type={d.get('update_type')}"))
        client.on_trade_result(lambda d: print(f"Trade result: {d.get('result', {})}"))

        # Place a market buy
        result = await client.buy_market("EURUSD", 0.01, sl=1.0800, tp=1.1200)
        print(f"Trade: {result}")

        await asyncio.sleep(10)


asyncio.run(main())

Calling login() while already logged in raises SessionError; call logout() before switching accounts. A successful logout() clears stored reconnect credentials. A rejected logout raises SessionError and retains the current session state and credentials.

Trading Examples

Trade request wire format

MT5WebClient() uses the legacy trade request format by default. For the build 6090 market-execution environment reported in PR #1, opt in explicitly:

client = MT5WebClient(trade_protocol="build_6090")

This profile adds the observed reserved bytes and uses action TRADE_ACTION_MARKET_DEAL (3) for buy_market, sell_market, and close_position. The report covers selected symbols on one demo server; compatibility with other brokers, execution modes, and builds has not been established. The client does not select a profile from the handshake build number.

trade_request() and order_send() keep the action and fields supplied by the caller. For example, callers using order_send() for a market-execution deal with this profile must explicitly set action to TRADE_ACTION_MARKET_DEAL. The profile does not set action_id or type_flags automatically; their requirements may depend on the server. The PR's observed official-client requests used an action_id starting at 1000 and type_flags=2; whether these values are required has not been established. The buy_market(), sell_market(), and close_position() helpers accept optional action_id and type_flags keywords when a server requires them; both default to zero. Trade requests and acknowledgements do not establish that an order was filled; inspect subsequent trade results and positions.

close_position() rejects non-positive position IDs. If order_type is omitted, it looks up the position and raises ValidationError without placing a trade if the position, its direction, or the position query is unavailable, or if the position's symbol is missing or differs from the requested symbol. Pass an explicit order_type only when the intended closing side is known.

from pymt5 import (
    MT5WebClient, ORDER_TYPE_BUY, ORDER_TYPE_SELL,
    TRADE_RETCODE_DONE, ORDER_FILLING_IOC,
)

# Market orders
result = await client.buy_market("XAUUSD", 0.1, sl=2300.0, tp=2400.0)
result = await client.sell_market("EURUSD", 0.05, deviation=30)

# Pending orders
result = await client.buy_limit("EURUSD", 0.1, price=1.0800, sl=1.0750, tp=1.0900)
result = await client.sell_stop("GBPUSD", 0.1, price=1.2500)

# Stop-limit orders (trigger at price, then place limit at stop_limit_price)
result = await client.buy_stop_limit("EURUSD", 0.1, price=1.1000,
                                      stop_limit_price=1.0950, sl=1.0900, tp=1.1100)
result = await client.sell_stop_limit("GBPUSD", 0.1, price=1.2400,
                                       stop_limit_price=1.2450)

# Close position (auto-detects BUY/SELL direction)
result = await client.close_position("EURUSD", position_id=123456, volume=0.1)

# Close by opposite position (hedge netting)
result = await client.close_position_by("EURUSD", position_id=123456,
                                         position_by=789012)

# Modify SL/TP
result = await client.modify_position_sltp("EURUSD", position_id=123456,
                                            sl=1.0850, tp=1.0950)

# Cancel pending order
result = await client.cancel_pending_order(order=789012)

Push Notifications

# Combined position+order updates in one callback
def on_trade_change(data):
    print(f"Positions: {len(data['positions'])}, Orders: {len(data['orders'])}")
client.on_trade_update(on_trade_change)

# Trade transaction (order add/update/delete, balance updates)
def on_transaction(data):
    if data.get('update_type') == 2:
        print(f"Balance update: {data['balance_info']}")
    else:
        print(f"Order transaction: type={data.get('transaction_type')}")
client.on_trade_transaction(on_transaction)

# Account balance/equity changes
client.on_account_update(lambda d: print(f"Balance: {d.get('balance')}"))

# Extended symbol data with options greeks
client.on_symbol_details(lambda d: print(f"Delta: {d[0].get('delta')}"))

# Async trade execution results
client.on_trade_result(lambda d: print(f"Retcode: {d.get('result', {}).get('retcode')}"))

# Order book (DOM) updates
client.on_book_update(lambda entries: print(f"Book: {len(entries)} symbols"))

# Login status changes (e.g. forced logout)
client.on_login_status(lambda r: print(f"Login status: code={r.code}"))

Protocol Flow

  1. connect() — opens WebSocket, sends bootstrap (cmd=0), receives new AES key
  2. login() — sends credentials (cmd=28), receives session token, starts heartbeat
  3. Data commands (get_account, get_symbols, get_rates, get_positions_and_orders, etc.)
  4. close() — stops heartbeat, sends logout (cmd=2), closes WebSocket

Important: Do NOT call init_session() (cmd=29) after login — that command is for demo/real account creation, not session initialization.

Logging

import logging
logging.basicConfig(level=logging.DEBUG)
# Loggers: pymt5.client, pymt5.transport

Examples

Run any example from the project root:

pip install -e .
python examples/01_connect_and_account.py
Example Description
01_connect_and_account.py Login, full account info (cmd=3), positions/orders/deals, ping
02_market_data.py Symbol groups (cmd=9), symbol cache, spreads (cmd=20), OHLCV rates
03_realtime_ticks.py Tick subscription, account/symbol details/login status push handlers
04_trading.py Market/limit/stop orders, modify SL/TP, position close
05_order_book.py Order book (DOM) subscription (cmd=22/23)
06_all_push_notifications.py All 9 push notification types in one example
07_all_order_types.py All 9 order types + modify + cancel + close + raw trade_request
08_misc_features.py Corporate links, notifications, trader params, verify code, reconnect

Command Coverage

CMD Name Status
0 Bootstrap Implemented
2 Logout Implemented
3 Get Account NEW — balance, credit, currency, leverage, name
4 Get Positions/Orders Implemented
5 Get Trade History Implemented
6 Get Symbols Implemented
7 Subscribe Ticks Implemented
8 Tick Push Implemented
9 Get Symbol Groups NEW — Forex, Metals, Indexes, etc.
10 Trade Update Push NEW — order add/update/delete + balance updates
11 Get Rates Implemented
12 Trade Request Implemented
13 Symbol Update Push Implemented
14 Account Update Push NEW — real-time balance/equity changes
15 Login Status Push Implemented
17 Symbol Details Push NEW — extended quotes with options greeks
18 Get Full Symbols Implemented
19 Trade Result Push NEW — async trade execution results
20 Get Spreads IMPROVED — proper schema parsing
22 Subscribe Book NEW — order book / DOM subscription
23 Book Push NEW — order book push with bid/ask levels
24 Change Password Implemented
27 Verify Code Implemented
28 Login Implemented
29 Init Session Implemented
30 Open Demo Implemented
39 Open Real Account Implemented
40 Send Verification Codes Implemented
34 Get Symbols (gzip) Implemented
41 Trader Params Implemented
42 Notify Implemented
43 OTP Setup / Disable Implemented
44 Get Corporate Links NEW — broker links
51 Ping Implemented
52 Reserved Bootstrap Command EXPERIMENTAL — bootstrap-only helper with unknown business meaning

Commands 21, 25, 33, 37, 50, 52, 100-112 are still accepted by the transport allowlist but have no direct usage in the current public Web Terminal JavaScript source. Live probing on 2026-03-16 showed that 21, 25, 33, 37, 50, 100-112 time out with an empty payload, while 52 only returns an empty ACK on a pristine bootstrap-only socket and causes the server to drop the connection after cmd=29 or cmd=28. Commands 39, 40, and 43 are now implemented in pymt5.

Documentation

Full API documentation is available on GitHub Pages.

To build docs locally:

pip install -e ".[docs]"
cd docs && make html

CI/CD

This project uses GitHub Actions for continuous integration, testing across:

Python 3.11 Python 3.12 Python 3.13
Linux (ubuntu-latest) ✅ ✅ ✅
macOS (macos-latest) ✅ ✅ ✅
Windows (windows-latest) ✅ ✅ ✅

The CI pipeline also runs mypy type checking and builds the Sphinx documentation. For a local pre-push pass, run:

make check

Notes

  • This package is experimental and tracks the MT5 Web Terminal protocol as observed in the public Web Terminal build 5687 (built on 2026-03-15)
  • MetaQuotes may change the protocol at any time
  • Volume encoding: MT5 uses integer volumes where volume = lots × 10^precision. The MetaQuotes demo server uses precision=8, so 1.0 lot = 100,000,000 and 0.01 lot = 1,000,000
  • get_account() (cmd=3) returns balance, credit, currency, leverage, and name. Equity, margin, and profit are computed from positions
  • The Symbol Details push (cmd=17) schema may produce approximate values — the exact byte layout for options greeks varies by server
  • Order book (cmd=22/23) may not produce pushes on all servers — depends on broker configuration
  • Some commands like trader_params (cmd=41) may cause the server to close the connection on rapid successive calls

License

MIT License — see LICENSE for details.

Metadata

Release files for mt5-web-client 1.0.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mt5-web-client 1.0.4
File Size Uploaded Provenance Actions
mt5_web_client-1.0.4.tar.gz 171.6 kB With provenance Details for mt5_web_client-1.0.4.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for mt5-web-client 1.0.4
File Interpreter ABI Platform Actions
mt5_web_client-1.0.4-py3-none-any.whl Python 3 none any Details for mt5_web_client-1.0.4-py3-none-any.whl

Total release size: 252.4 kB

Release files / mt5_web_client-1.0.4.tar.gz

Download URL mt5_web_client-1.0.4.tar.gz
Size 171.6 kB
Tags Source
SHA-256 checksum
How to use checksums
6b84013d7687b6b056dc51f5e1bb4ffc7579a5ea6a8d9c923550da685bd2803d
BLAKE2b-256 checksum
How to use checksums
f435260cd043153367ca122c41e93be966c37b55fd436e4fc5c1414537247421
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 13, 2026.

Transparency log

Release files / mt5_web_client-1.0.4-py3-none-any.whl

Download URL mt5_web_client-1.0.4-py3-none-any.whl
Size 80.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
372e087b7da9375e13473977195dca8790ee12877d13ee9eef0d5d0cf916ae0c
BLAKE2b-256 checksum
How to use checksums
48143866b18382dce494a7565a40e943b58f89a7de8b51a01718dede1d9e054d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 13, 2026.

Transparency log

Security for mt5-web-client 1.0.4

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for mt5_web_client-1.0.4.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 13, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.4 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page