Skip to main content

Kite Connect WebSocket: Live Tick Data in Python

FB THEFINBABA

Getting the first tick to print on your screen takes about fifteen lines. Getting a ticker that survives a full session, every session, is a different piece of work — and it is where most retail systems quietly fail.

This covers the parts after the first fifteen lines: the modes and what they cost you in bandwidth, why your strategy must not live inside the callback, reconnection, detecting a feed that has gone silent, building candles from ticks, and keeping the process alive.

Why WebSocket rather than polling

The obvious approach is to call the quote endpoint in a loop. It works for about a day.

Brokers cap requests per second, and a loop across a watchlist of any size hits that ceiling quickly. Worse, it hits it unpredictably — you get throttled on the busy morning when your strategy actually needed the data, not on the quiet afternoon when you were testing.

A WebSocket inverts the arrangement. You open one connection, tell the server which instruments you care about, and it pushes updates to you as they happen. One connection, no polling, no rate limit on the incoming data, and latency measured in milliseconds rather than in your loop interval.

The minimum working ticker

The shape of it, using the official client:

from kiteconnect import KiteTicker

kws = KiteTicker(api_key, access_token)

TOKENS = [256265]           # NIFTY 50 index

def on_connect(ws, response):
    ws.subscribe(TOKENS)
    ws.set_mode(ws.MODE_FULL, TOKENS)

def on_ticks(ws, ticks):
    for t in ticks:
        print(t['instrument_token'], t['last_price'])

def on_close(ws, code, reason):
    print('closed', code, reason)

def on_error(ws, code, reason):
    print('error', code, reason)

kws.on_connect = on_connect
kws.on_ticks   = on_ticks
kws.on_close   = on_close
kws.on_error   = on_error

kws.connect()                # blocks the thread

Note that connect() blocks. Called like this it takes over the thread, which is fine for a script that only streams and wrong for anything that also has to do work.

Tick modes, and what each one costs

Three modes, and the choice matters more than people expect once the instrument list grows.

  • LTP — last traded price only. Tiny payload. Correct choice when your rules only need the price.
  • Quote — adds open, high, low, close, volume and the last quantity. The usual middle ground.
  • Full — adds market depth, open interest and timestamps. Substantially heavier per tick.

Subscribing a large option chain in Full mode produces a great deal of data per second, and the cost lands in two places: bandwidth, and the time your callback spends processing each batch. If your rules do not read market depth, do not subscribe to it.

Modes can be set per instrument, which is the sensible arrangement. Full mode for the two or three instruments you actually trade, LTP for everything you are only watching.

Your strategy does not belong in the callback

This is the single most common mistake, and it is invisible until the market moves.

The ticker runs its own event loop. Whatever you do inside on_ticks happens on that loop, and while it is running no further ticks are processed. Put a backtest lookup, a database write or an order placement in there and every one of those blocks the feed. On a quiet day nothing appears wrong. During a fast move, when ticks arrive in bursts and your logic has the most to do, you fall behind and start acting on stale prices.

The fix is to make the callback do nothing but hand the data off:

import queue, threading

ticks_q = queue.Queue()

def on_ticks(ws, ticks):
    ticks_q.put(ticks)        # nothing else, ever

def worker():
    while True:
        batch = ticks_q.get()
        if batch is None:
            break
        handle(batch)         # strategy, storage, orders

threading.Thread(target=worker, daemon=True).start()
kws.connect(threaded=True)    # ticker on its own thread

Now the feed thread only enqueues, and slow work happens elsewhere. If the queue starts growing, that is a measurable signal your processing is too slow — log its size periodically and you will know before the market tells you.

Reconnection is not optional

WebSocket connections drop. Not rarely — routinely, from network hiccups, from the broker's side, from your own machine sleeping. A ticker without reconnection handling is a ticker that works until the first time it matters.

The client supports automatic reconnection with exponential backoff, and it should be switched on explicitly:

kws.connect(threaded=True,
            disable_ssl_verification=False)

# reconnect settings
kws.enable_reconnect(max_delay=60, max_tries=50)

def on_reconnect(ws, attempts_count):
    print('reconnecting, attempt', attempts_count)

def on_noreconnect(ws):
    print('gave up reconnecting - alert someone')

kws.on_reconnect   = on_reconnect
kws.on_noreconnect = on_noreconnect

Two things people forget. First, on_connect fires again after a reconnect, so your subscribe and set_mode calls must live in there rather than being run once at startup — otherwise you reconnect to a feed you are no longer subscribed to. Second, on_noreconnect means the ticker has stopped trying. That is the point at which something should alert you and your open positions should be handled deliberately, not the point at which your program carries on silently with no data.

Detecting a feed that has gone quiet

The dangerous failure is not the disconnect you are told about. It is the connection that stays open and stops delivering.

From your program's point of view a silent feed and a still market look identical. If your strategy is waiting for a condition, it simply never triggers, and you find out at the end of the day that nothing ran.

The defence is a watchdog on the timestamp of the last tick:

import time, threading

last_tick_at = time.time()

def on_ticks(ws, ticks):
    global last_tick_at
    last_tick_at = time.time()
    ticks_q.put(ticks)

def watchdog(max_silence=30):
    while True:
        time.sleep(5)
        if not market_is_open():
            continue
        silence = time.time() - last_tick_at
        if silence > max_silence:
            alert(f'no ticks for {int(silence)}s')
            kws.close()      # force a reconnect cycle

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

Set the threshold against the instrument. A liquid index ticks constantly and thirty seconds of silence is clearly wrong; an illiquid option strike may legitimately go minutes without a trade, and a watchdog tuned for the index will produce false alarms on it.

Building candles from ticks

Ticks are not candles, and most strategies think in candles. Aggregating them yourself is straightforward and worth doing correctly, because the alternative — requesting historical candles repeatedly during the session — is slow and burns rate limit.

from collections import defaultdict
import datetime as dt

candles = defaultdict(dict)      # token -> minute -> ohlcv

def add_tick(token, price, qty, ts):
    minute = ts.replace(second=0, microsecond=0)
    c = candles[token].get(minute)
    if c is None:
        candles[token][minute] = {
            'o': price, 'h': price, 'l': price,
            'c': price, 'v': qty}
    else:
        c['h'] = max(c['h'], price)
        c['l'] = min(c['l'], price)
        c['c'] = price
        c['v'] += qty

One rule that saves a great deal of confusion: act on a candle only once it has closed. A minute candle at 10:30:20 is incomplete, and a rule that reads its high will behave differently every time you run it. Wait for the minute to roll over, then evaluate.

Use the exchange timestamp on the tick rather than your own clock. Your server's time and the exchange's are not the same thing, and the difference lands exactly on the candle boundary.

Storing ticks without filling the disk

A full-mode subscription across a busy instrument list produces millions of records in a session. Writing each one to a database as it arrives will not keep up, and writing each one to a CSV will produce a file nobody can open by Friday.

What works in practice: buffer in memory and flush in batches, write to a columnar format such as Parquet rather than text, partition files by date and instrument, and store only the fields you will actually read later. Depth data is enormous and almost nobody analyses it afterwards.

The honest question to ask first is whether you need raw ticks at all. For most strategies, storing the minute candles you built above is enough, and it is roughly three orders of magnitude smaller.

Keeping the process alive

Everything above assumes the program is running. On a laptop that assumption fails daily.

Production means a small cloud server with the program supervised by systemd or a similar manager, started at a fixed time each morning, restarted automatically if it exits, and logging to a file you can read afterwards. Add the static IP your broker requires registered for API order flow, and the setup is complete.

Also handle the access token, which expires each session. A ticker that starts at 9:00 with yesterday's token connects to nothing, and the failure is silent unless you check.

All of this needs a broker account with API access behind it, which is what supplies both the token and the feed.

Feed plus order endpoints

The WebSocket runs on a Zerodha account

Kite Connect is billed separately from the account itself. Account free to open

The ticker, the queue pattern, candle aggregation and the deployment around them are all covered in our Algorithmic Trading with Python course at Rs 24,900 — one payment, lifetime access, free demo on WhatsApp first.

We sell no tips and no signal group, we manage nobody's money, and we promise no returns. Trading carries a real risk of loss.

Disclosure: the account-opening link on this page is under Atul Shrivastava's Zerodha Authorised Person registration (NSE AP Reg: AP2516003481; Zerodha Broking Ltd. SEBI Reg: INZ000031633) and earns a revenue share. TheFinBaba is not a SEBI-registered Investment Adviser — this content is educational, not investment advice.

Frequently Asked Questions

How many instruments can I subscribe to on the Kite Connect WebSocket?

The client supports a large subscription list, but the practical limit is what your program can process without falling behind. Mode matters more than count - Full mode across a big option chain produces far more data per second than LTP mode across the same list.

Why does my ticker stop receiving data after some time?

Usually a dropped connection without reconnection handling, or an expired access token. Enable automatic reconnection, put your subscribe and set_mode calls inside on_connect so they re-run after a reconnect, and add a watchdog on the time since the last tick.

Should I place orders inside the on_ticks callback?

No. The callback runs on the ticker's event loop and blocks the feed while it executes. Push ticks onto a queue and do all strategy work, storage and order placement on a separate thread.

How do I build one-minute candles from tick data?

Group ticks by the minute of the exchange timestamp, keeping first price as open, running max and min as high and low, latest as close, and summing quantity for volume. Evaluate rules only after the minute has closed, never mid-candle.

Do I need to store raw ticks?

Usually not. Minute candles are around three orders of magnitude smaller and enough for most strategies. If you do store ticks, buffer and flush in batches, use a columnar format, partition by date, and drop the depth fields you will never read.

What is the difference between LTP, Quote and Full mode?

LTP sends only the last traded price. Quote adds open, high, low, close, volume and last quantity. Full adds market depth, open interest and timestamps, at a significantly larger payload. Set modes per instrument rather than one mode for everything.

Disclaimer: TheFinBaba provides educational content only - this is not investment advice. Trading involves risk of loss.

Atul Shrivastava
Written by

Atul Shrivastava

Founder & Lead Trainer, TheFinBaba

16+ years in the markets. 8+ years teaching Python algo trading.

Full profile

Found this useful? Share it:

WhatsApp Share

Disclaimer: TheFinBaba provides educational content only. Nothing in this article is investment advice or a recommendation to buy or sell any security. Trading in financial markets carries risk of loss — make every decision based on your own research and risk capacity.

Free Account Opening

Open a Free Demat & Trading Account in 5 Minutes

Start trading stocks, F&O, IPOs, bonds & ETFs with Zerodha — India's most trusted discount broker. Zero account-opening fee, paperless Aadhaar KYC, and the best APIs (Kite Connect) for Python algo trading.

  • Zero account-opening fee
  • ₹0 brokerage on equity delivery
  • Kite Connect API — best for Python algo traders
  • Paperless Aadhaar e-KYC in 5 minutes
Open Free Demat Account

* Zerodha is SEBI-registered. Account opening subject to KYC approval. Atul Shrivastava is an Authorised Person (AP) — Reg AP2516003481.

Trusted by 1.6 Cr+ Indian Investors

Zerodha is India's largest stock broker by active clients (NSE data, 2026).

₹0
Delivery
₹20
Intraday
5 min
Opening