10. WebSocket Market Data

Receive trade prices, quotes, and foreign room data in real-time via WebSocket

Objective

Receive real-time tick data (trade prices, order book quotes, foreign room) via WebSocket. No OTP required for market data.

Flow

Client ↔ Streaming API ↔ Market Data Event
  1. Open the WebSocket connection with a valid token (no OTP needed)
  2. Subscribe by symbol (subscribe_symbol), by the constituents of an index (subscribe_index) and by the index's own ticks (subscribe_index_ticks)
  3. The server pushes events on new trades or quote changes
  4. The on_data callback dispatches by type: IndexTickMessage, TradeMessage, QuoteMessage, ForeignRoomMessage
  5. On disconnect, the SDK reconnects

Message Types

TypeDescription
TradeMessageTrade price, volume, buy/sell side
QuoteMessageBid/ask order book
ForeignRoomMessageForeign room remaining
IndexTickMessageTick of the index itself (points, change), received via subscribe_index_ticks
HeartbeatMessageServer keepalive

Since SDK 3.3.0, subscribe_index(["VN30"]) sends trade/quote/room.<code>@*, so it receives data for the index constituents (TradeMessage, QuoteMessage...), not the index tick. To get the tick of the index itself (e.g. VNINDEX), use subscribe_index_ticks. See also Advanced WebSocket.

Sample Code — Sync

python/sample_10_websocket_data.py
from ssi_sdk import Auth, Stream, Config
from auth_helper import ensure_auth
from ssi_sdk.models.streaming import (
    TradeMessage, QuoteMessage, ForeignRoomMessage, IndexTickMessage,
)

config = Config(
    client_id="<your_client_id>",
    api_key="<your_api_key>",
    api_secret="<your_api_secret>",
    private_key="<your_private_key>",
)

def on_market_data(msg):
    if isinstance(msg, IndexTickMessage):
        # Tick of the index itself (subscribe_index_ticks), NOT a constituent tick
        print(f"  [INDEX] {msg.symbol} | Value: {msg.value} | Change: {msg.change} ({msg.change_percent}%)")
    elif isinstance(msg, TradeMessage):
        print(f"  [TRADE] {msg.symbol} | Price: {msg.price} | Qty: {msg.quantity}")
    elif isinstance(msg, QuoteMessage):
        print(f"  [QUOTE] {msg.symbol} | Bid: {msg.bid_prices[:3]} | Ask: {msg.ask_prices[:3]}")
    elif isinstance(msg, ForeignRoomMessage):
        print(f"  [ROOM]  {msg.symbol} | Room: {msg.current_room}/{msg.total_room}")

with Auth(config) as auth:
    ensure_auth(auth)
    with Stream(auth) as stream:
        stream.streaming.connect()
        stream.streaming.on_data = on_market_data
        stream.streaming.subscribe_symbol(["SSI", "HPG", "VNM"])
        # Since SDK 3.3.0, subscribe_index returns the CONSTITUENTS' data (e.g. VN30)
        stream.streaming.subscribe_index(["VN30"])
        # For the index's own tick (IndexTickMessage) use subscribe_index_ticks
        stream.streaming.subscribe_index_ticks(["VNINDEX"])
        try:
            stream.streaming.wait(timeout_ms=300_000)  # 5 minutes
        except KeyboardInterrupt:
            pass

Sample Code — Async

python/sample_10_websocket_data_async.py
import asyncio
from ssi_sdk import AsyncAuth, AsyncStream, Config
from auth_helper import ensure_auth_async
from ssi_sdk.models.streaming import TradeMessage, QuoteMessage

config = Config(...)

async def main():
    async with AsyncAuth(config) as auth:
        await ensure_auth_async(auth)
        async with AsyncStream(auth) as stream:
            await stream.streaming.connect()
            stream.streaming.on_data = lambda msg: print(f"  {msg}")
            await stream.streaming.subscribe_symbol(["SSI", "HPG"])
            await stream.streaming.subscribe_index(["VN30"])           # constituents of VN30
            await stream.streaming.subscribe_index_ticks(["VNINDEX"])  # the index's own tick
            await stream.streaming.wait(timeout_ms=300_000)

asyncio.run(main())

Notes

  • No OTP required for market data streaming.
  • SDK handles reconnect automatically.
  • Use wait(timeout_ms=...) to limit listening duration. Since SDK 3.3.0 the unit is milliseconds; the old timeout= (seconds) was removed and raises TypeError.
  • subscribe_index receives constituents' data; subscribe_index_ticks receives the index's own tick. For board, index summary, session flag, candles and connection events see Advanced WebSocket.

On this page