Skip to content

Stream Real-Time Symbol Ticks

Request: subscribe to real-time tick data for specified symbols.

API Information:

  • Python API: MT5Account.on_symbol_tick(...) (defined in package/MetaRpcMT5/helpers/mt5_account.py)
  • gRPC service: mt5_term_api.SubscriptionService
  • Proto definition: OnSymbolTick (defined in mt5-term-api-subscriptions.proto)

RPC

  • Service: mt5_term_api.SubscriptionService
  • Method: OnSymbolTick(OnSymbolTickRequest) -> stream OnSymbolTickReply
  • Low-level client (generated): SubscriptionServiceStub.OnSymbolTick(request, metadata)

💬 Just the essentials

  • What it is. Real-time stream of price ticks for specified symbols.
  • Why you need it. Monitor live prices, implement tick-based strategies, track market movements.
  • High frequency. Can generate many updates per second during active trading.

🎯 Purpose

Use it to:

  • Monitor real-time price changes for symbols
  • Implement tick-based trading strategies
  • Track bid/ask spreads in real-time
  • Build live price dashboards
  • Detect price movements immediately
  • Collect tick data for analysis
from MetaRpcMT5 import MT5Account

class MT5Account:
    # ...

    async def on_symbol_tick(
        self,
        symbols: list[str],
        cancellation_event: Optional[asyncio.Event] = None,
    ):
        """
        Subscribes to real-time tick data for specified symbols.

        Yields:
            OnSymbolTickData: Async stream of tick data responses.
        """

Request message:

message OnSymbolTickRequest {
  repeated string symbol_names = 1;  // List of symbols to subscribe to
}

🔽 Input

Parameter Type Description
symbols list[str] Symbol names to subscribe to (e.g., ["EURUSD", "GBPUSD"])
cancellation_event Optional[asyncio.Event] Event to cancel streaming (optional)

⬆️ Output - Async Generator

Returns an async generator that yields OnSymbolTickData objects.

OnSymbolTickData fields:

Field Type Description
symbol_tick MrpcSubscriptionMqlTick Tick data (see below)
terminal_instance_guid_id str Terminal instance identifier

MrpcSubscriptionMqlTick fields:

Field Type Description
symbol str Symbol name
time google.protobuf.Timestamp Time of last price update
time_msc int64 Time of last price update (milliseconds)
bid float64 Current Bid price
ask float64 Current Ask price
last float64 Price of last deal (Last)
volume uint64 Volume for the current Last price
volume_real float64 Volume for the current Last price (real)
flags uint32 Tick flags (buy/sell indication)

📚 Tutorial

For a detailed line-by-line explanation with examples, see: OnSymbolTick - How it works


🧩 Notes & Tips

  • Automatic reconnection: All MT5Account streaming methods have built-in protection against transient gRPC errors with automatic reconnection via execute_stream_with_reconnect.
  • Async generator: The method returns an async generator - use async for to consume data.
  • Multiple symbols: You can subscribe to multiple symbols in a single stream.
  • Cancellation: Pass an asyncio.Event and call event.set() to stop the stream gracefully.
  • High frequency: Tick streams can be very high frequency - ensure your processing is efficient.
  • Resource management: Always cancel streams when done to free resources.
  • Error handling: Errors are raised as exceptions - wrap in try/except for error handling.
  • UUID handling: The terminal instance UUID is auto-generated by the server if not provided. For explicit control (e.g., in streaming scenarios), pass id_=uuid4() to constructor.

🔗 Usage Examples

1) Monitor live ticks for EURUSD

import asyncio
from MetaRpcMT5 import MT5Account

async def monitor_ticks():
    account = MT5Account(
        user=12345,
        password="password",
        grpc_server="mt5.mrpc.pro:443"
    )
    await account.connect_by_server_name(
        server_name="YourBroker-Demo",
        base_chart_symbol="EURUSD"
    )

    try:
        # Subscribe to EURUSD ticks
        async for tick_data in account.on_symbol_tick(symbols=["EURUSD"]):
            tick = tick_data.symbol_tick
            print(f"{tick.symbol}: Bid={tick.bid:.5f}, Ask={tick.ask:.5f}, "
                  f"Spread={tick.ask - tick.bid:.5f}")

    except KeyboardInterrupt:
        print("Stopping tick stream...")

    finally:
        await account.channel.close()

# Run
asyncio.run(monitor_ticks())

2) Monitor multiple symbols with cancellation

import asyncio
from MetaRpcMT5 import MT5Account

async def monitor_multiple_symbols():
    account = MT5Account(
        user=12345,
        password="password",
        grpc_server="mt5.mrpc.pro:443"
    )
    await account.connect_by_server_name(
        server_name="YourBroker-Demo",
        base_chart_symbol="EURUSD"
    )

    # Create cancellation event
    cancel_event = asyncio.Event()

    # Stop after 30 seconds
    async def auto_cancel():
        await asyncio.sleep(30)
        cancel_event.set()
        print("\nStopping stream...")

    try:
        # Start auto-cancel task
        cancel_task = asyncio.create_task(auto_cancel())

        # Subscribe to multiple symbols
        symbols = ["EURUSD", "GBPUSD", "USDJPY"]

        async for tick_data in account.on_symbol_tick(
            symbols=symbols,
            cancellation_event=cancel_event
        ):
            tick = tick_data.symbol_tick
            spread_pips = (tick.ask - tick.bid) * 10000  # for 4-digit pairs

            print(f"[{tick.time_msc}] {tick.symbol}: "
                  f"Bid={tick.bid:.5f}, Ask={tick.ask:.5f}, "
                  f"Spread={spread_pips:.1f} pips")

        await cancel_task

    finally:
        await account.channel.close()

asyncio.run(monitor_multiple_symbols())

3) Track bid-ask spread statistics

import asyncio
from collections import defaultdict
from MetaRpcMT5 import MT5Account

async def track_spread_stats():
    account = MT5Account(
        user=12345,
        password="password",
        grpc_server="mt5.mrpc.pro:443"
    )
    await account.connect_by_server_name(
        server_name="YourBroker-Demo",
        base_chart_symbol="EURUSD"
    )

    # Statistics storage
    spreads = defaultdict(list)
    cancel_event = asyncio.Event()

    async def stop_after_duration(seconds):
        await asyncio.sleep(seconds)
        cancel_event.set()

    try:
        # Stop after 60 seconds
        stop_task = asyncio.create_task(stop_after_duration(60))

        symbols = ["EURUSD", "GBPUSD"]

        async for tick_data in account.on_symbol_tick(
            symbols=symbols,
            cancellation_event=cancel_event
        ):
            tick = tick_data.symbol_tick
            spread = tick.ask - tick.bid
            spreads[tick.symbol].append(spread)

        await stop_task

        # Print statistics
        print("\n=== Spread Statistics ===")
        for symbol, spread_list in spreads.items():
            avg_spread = sum(spread_list) / len(spread_list)
            min_spread = min(spread_list)
            max_spread = max(spread_list)

            print(f"\n{symbol}:")
            print(f"  Ticks received: {len(spread_list)}")
            print(f"  Average spread: {avg_spread * 10000:.1f} pips")
            print(f"  Min spread: {min_spread * 10000:.1f} pips")
            print(f"  Max spread: {max_spread * 10000:.1f} pips")

    finally:
        await account.channel.close()

asyncio.run(track_spread_stats())

4) Price alert system

import asyncio
from MetaRpcMT5 import MT5Account

async def price_alert_system():
    account = MT5Account(
        user=12345,
        password="password",
        grpc_server="mt5.mrpc.pro:443"
    )
    await account.connect_by_server_name(
        server_name="YourBroker-Demo",
        base_chart_symbol="EURUSD"
    )

    # Set price alerts
    alerts = {
        "EURUSD": {"above": 1.1000, "below": 1.0900},
        "GBPUSD": {"above": 1.2700, "below": 1.2500},
    }

    triggered = set()

    try:
        async for tick_data in account.on_symbol_tick(
            symbols=list(alerts.keys())
        ):
            tick = tick_data.symbol_tick
            symbol = tick.symbol

            if symbol not in alerts:
                continue

            # Check above threshold
            if tick.bid >= alerts[symbol]["above"]:
                alert_key = f"{symbol}_above"
                if alert_key not in triggered:
                    print(f"\n[ALERT] {symbol} bid {tick.bid:.5f} "
                          f"is ABOVE {alerts[symbol]['above']:.5f}")
                    triggered.add(alert_key)

            # Check below threshold
            if tick.bid <= alerts[symbol]["below"]:
                alert_key = f"{symbol}_below"
                if alert_key not in triggered:
                    print(f"\n[ALERT] {symbol} bid {tick.bid:.5f} "
                          f"is BELOW {alerts[symbol]['below']:.5f}")
                    triggered.add(alert_key)

            # Stop if all alerts triggered
            if len(triggered) == len(alerts) * 2:
                print("\nAll alerts triggered. Stopping...")
                break

    finally:
        await account.channel.close()

asyncio.run(price_alert_system())

5) Real-time tick logger to CSV

import asyncio
import csv
from datetime import datetime
from MetaRpcMT5 import MT5Account

async def log_ticks_to_csv():
    account = MT5Account(
        user=12345,
        password="password",
        grpc_server="mt5.mrpc.pro:443"
    )
    await account.connect_by_server_name(
        server_name="YourBroker-Demo",
        base_chart_symbol="EURUSD"
    )

    cancel_event = asyncio.Event()

    # Stop after 5 minutes
    async def auto_stop():
        await asyncio.sleep(300)
        cancel_event.set()

    try:
        stop_task = asyncio.create_task(auto_stop())

        # Open CSV file
        with open('ticks.csv', 'w', newline='') as csvfile:
            writer = csv.writer(csvfile)
            writer.writerow(['Timestamp', 'Symbol', 'Bid', 'Ask', 'Last', 'Volume'])

            tick_count = 0

            async for tick_data in account.on_symbol_tick(
                symbols=["EURUSD"],
                cancellation_event=cancel_event
            ):
                tick = tick_data.symbol_tick

                # Write to CSV
                writer.writerow([
                    datetime.fromtimestamp(tick.time_msc / 1000).isoformat(),
                    tick.symbol,
                    tick.bid,
                    tick.ask,
                    tick.last,
                    tick.volume_real
                ])

                tick_count += 1

                if tick_count % 100 == 0:
                    print(f"Logged {tick_count} ticks...")
                    csvfile.flush()  # Flush to disk

        await stop_task
        print(f"\nTotal ticks logged: {tick_count}")

    finally:
        await account.channel.close()

asyncio.run(log_ticks_to_csv())

6) Concurrent tick monitoring with tasks

import asyncio
from MetaRpcMT5 import MT5Account

async def process_eurusd_ticks(account):
    """Process EURUSD ticks"""
    async for tick_data in account.on_symbol_tick(symbols=["EURUSD"]):
        tick = tick_data.symbol_tick
        print(f"[EUR] {tick.bid:.5f}")
        await asyncio.sleep(0.1)  # Throttle output

async def process_gbpusd_ticks(account):
    """Process GBPUSD ticks"""
    async for tick_data in account.on_symbol_tick(symbols=["GBPUSD"]):
        tick = tick_data.symbol_tick
        print(f"[GBP] {tick.bid:.5f}")
        await asyncio.sleep(0.1)  # Throttle output

async def main():
    account = MT5Account(
        user=12345,
        password="password",
        grpc_server="mt5.mrpc.pro:443"
    )
    await account.connect_by_server_name(
        server_name="YourBroker-Demo",
        base_chart_symbol="EURUSD"
    )

    try:
        # Run multiple streams concurrently
        await asyncio.gather(
            process_eurusd_ticks(account),
            process_gbpusd_ticks(account),
        )

    except KeyboardInterrupt:
        print("\nStopping streams...")

    finally:
        await account.channel.close()

asyncio.run(main())

📚 See also