Build a watchlist manager

A watchlist is just a named set of instrument tokens your bot tracks — for signals, alerts, or WebSocket subscription (chapter 37). Keep it as a small, explicit config rather than scattering symbol strings through strategy code.

# watchlist.py
import json
from pathlib import Path

class Watchlist:
    def __init__(self, path: str = "data/watchlist.json"):
        self.path = Path(path)
        self.symbols: dict[str, int] = {}   # "NSE:INFY" -> instrument_token
        if self.path.exists():
            self.symbols = json.loads(self.path.read_text())

    def add(self, exchange: str, tradingsymbol: str, instrument_token: int):
        self.symbols[f"{exchange}:{tradingsymbol}"] = instrument_token
        self._save()

    def remove(self, exchange: str, tradingsymbol: str):
        self.symbols.pop(f"{exchange}:{tradingsymbol}", None)
        self._save()

    def tokens(self) -> list[int]:
        return list(self.symbols.values())

    def _save(self):
        self.path.parent.mkdir(exist_ok=True)
        self.path.write_text(json.dumps(self.symbols, indent=2))
wl = Watchlist()
wl.add("NSE", "INFY", 408065)
wl.add("NSE", "RELIANCE", 738561)
print(wl.tokens())   # feed directly into kws.subscribe(...)

Build it from a rule, not just manually

For anything beyond a handful of hand-picked names — e.g. "top 50 by volume," "all NIFTY constituents," "all stocks with F&O available" — generate the watchlist programmatically from the instrument master (chapter 21) plus a filter, and refresh it periodically rather than maintaining it by hand:

def nifty50_watchlist(nse_df: pd.DataFrame, constituents: list[str]) -> dict[str, int]:
    subset = nse_df[nse_df.tradingsymbol.isin(constituents)]
    return {f"NSE:{row.tradingsymbol}": row.instrument_token for row in subset.itertuples()}

(NIFTY 50 constituents change periodically on index rebalancing — source the current list from NSE rather than hardcoding it long-term.)

Next: 036 — Connect to the WebSocket ticker