Skip to content

Realtime Orderbook Streaming

The orderbook is the raw microstructure of the market.

Every algo that optimises:

  • entry quality
  • slippage
  • execution timing
  • liquidity sensing

ultimately depends on live bid–ask depth, not just last traded price.

Nubra’s WebSocket Orderbook stream gives you tick-by-tick depth updates with deterministic structure, identical in UAT and LIVE.


What Is the Orderbook?

The 3 Layers of Market Truth

1 Last Trade

What just happened

Represents the most recent executed transaction in the market.

LTP

Last Traded Price (₹)

LTQ

Quantity traded at the last price

2 Bid Depth

Buy-side liquidity

Aggregated buy orders waiting in the market.

price

Bid price level

quantity

Total buy quantity at that price

num_orders

Number of active buy orders

3 Ask Depth

Sell-side liquidity

Aggregated sell orders waiting in the market.

price

Ask price level

quantity

Total sell quantity at that price

num_orders

Number of active sell orders


Realtime Orderbook Streaming via Nubra WebSocket

Realtime Orderbook — Visualised

This is a live WebSocket orderbook rendered in real time using matplotlib.

  • Tick-by-tick depth updates via Nubra WebSocket
  • 20 levels of Bid and Ask depth
  • Prices converted from paise → rupees
  • No polling, no snapshots — pure streaming

Ideal for execution analysis, liquidity sensing, and slippage-aware strategies.

WebSocket OrderBook Stream and Visualization

Below is a ready-to-use example that streams a realtime 20-level orderbook over WebSocket and visualizes it using Matplotlib.

The WebSocket runs in a separate background thread so socket.keep_running() can receive data continuously without blocking the UI. Incoming ticks update a shared snapshot, while Matplotlib periodically reads this snapshot to redraw the orderbook.

Change the REF_ID to visualize the orderbook for any instrument.

#!/usr/bin/env python3

import threading
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

from nubra_python_sdk.ticker import websocketdata
from nubra_python_sdk.start_sdk import InitNubraSdk, NubraEnv


# ============================================================
# CONFIG
# ============================================================

REF_ID = "1842210"
DEPTH = 20
UPDATE_MS = 300


# ============================================================
# Shared snapshot
# ============================================================

snapshot = {
    "ltp": "–",
    "ltq": "–",
    "volume": "–",
    "bids": [],
    "asks": [],
}

lock = threading.Lock()


# ============================================================
# WebSocket callback
# ============================================================

def on_orderbook_data(msg):
    with lock:
        snapshot["ltp"] = f"{msg.last_traded_price / 100:.2f}"
        snapshot["ltq"] = msg.last_traded_quantity
        snapshot["volume"] = msg.volume

        snapshot["bids"] = [
            (lvl.price / 100, lvl.quantity, lvl.num_orders)
            for lvl in msg.bids[:DEPTH]
        ]

        snapshot["asks"] = [
            (lvl.price / 100, lvl.quantity, lvl.num_orders)
            for lvl in msg.asks[:DEPTH]
        ]


def on_connect(msg):
    print("[WebSocket] Connected")


# ============================================================
# WebSocket thread
# ============================================================

def start_socket():
    nubra = InitNubraSdk(NubraEnv.UAT,env_creds=True)
    socket = websocketdata.NubraDataSocket(
        client=nubra,
        on_orderbook_data=on_orderbook_data,
        on_connect=on_connect,
    )
    socket.connect()
    socket.subscribe([REF_ID], data_type="orderbook")
    socket.keep_running()


threading.Thread(target=start_socket, daemon=True).start()


# ============================================================
# Matplotlib Layout (ULTRA COMPACT)
# ============================================================

plt.style.use("dark_background")
fig = plt.figure(figsize=(10, 6), facecolor="black")

# Header cards (shorter)
ax_ltp = fig.add_axes([0.05, 0.84, 0.25, 0.095])
ax_ltq = fig.add_axes([0.375, 0.84, 0.25, 0.095])
ax_vol = fig.add_axes([0.70, 0.84, 0.25, 0.095])

# Tables (taller)
ax_bid = fig.add_axes([0.05, 0.08, 0.42, 0.74])
ax_ask = fig.add_axes([0.53, 0.08, 0.42, 0.74])

for ax in (ax_ltp, ax_ltq, ax_vol, ax_bid, ax_ask):
    ax.axis("off")


# ============================================================
# UI helpers (SMALL TEXT)
# ============================================================

def draw_card(ax, title, value):
    ax.clear()
    ax.axis("off")

    ax.add_patch(
        plt.Rectangle(
            (0, 0), 1, 1,
            facecolor="#121212",
            edgecolor="#2a2a2a",
            linewidth=0.8
        )
    )

    ax.text(
        0.5, 0.62, title,
        ha="center", va="center",
        fontsize=8, color="#aaaaaa"
    )

    ax.text(
        0.5, 0.30, f"{value}",
        ha="center", va="center",
        fontsize=12, weight="bold", color="white"
    )


def draw_table(ax, title, rows, bg_color):
    ax.clear()
    ax.axis("off")

    ax.text(
        0.5, 0.985, title,
        ha="center", va="bottom",
        fontsize=9, weight="bold",
        transform=ax.transAxes
    )

    if not rows:
        rows = [["–", "–", "–"]]

    formatted = []
    for p, q, n in rows:
        price = f"{p:.2f}" if isinstance(p, (int, float)) else p
        formatted.append([price, q, n])

    table = ax.table(
        cellText=formatted,
        colLabels=["Price", "Qty", "Orders"],
        loc="center",
        cellLoc="center",
        colLoc="center",
    )

    table.scale(1, 1.05)

    for (row, col), cell in table.get_celld().items():
        cell.set_edgecolor("#2a2a2a")
        cell.set_linewidth(0.6)

        if row == 0:
            cell.set_facecolor("#1f1f1f")
            cell.set_text_props(color="white", weight="bold", fontsize=7)
        else:
            cell.set_facecolor(bg_color)
            cell.set_text_props(color="white", fontsize=7)

        cell.set_height(0.045)   # 🔥 fits 20 rows cleanly


# ============================================================
# UI Update Loop
# ============================================================

def update(_):
    with lock:
        ltp = snapshot["ltp"]
        ltq = snapshot["ltq"]
        vol = snapshot["volume"]
        bids = snapshot["bids"]
        asks = snapshot["asks"]

    draw_card(ax_ltp, "LTP", ltp)
    draw_card(ax_ltq, "LTQ", ltq)
    draw_card(ax_vol, "Volume", vol)

    draw_table(ax_bid, "Bid", bids, "#0f2f1b")
    draw_table(ax_ask, "Ask", asks, "#2f1414")


ani = FuncAnimation(
    fig,
    update,
    interval=UPDATE_MS,
    cache_frame_data=False
)

plt.show()
NEO Assistant