From c8cfc7702a9c199986f3f48a9f7894e4bfe0f50a Mon Sep 17 00:00:00 2001 From: Shrini Date: Fri, 27 Mar 2026 10:12:33 +0400 Subject: [PATCH 1/3] Fix ExchangeTimingData AttributeError and wrong segment for MCX/currency futures in Streamlit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three bugs caused Streamlit examples to fail while CLI equivalents worked fine: 1. ExchangeTimingData AttributeError (Market Holidays) ExchangeTimingData objects were being treated as dicts. Replaced the combined getattr/e.get() one-liner with explicit isinstance checks for str, dict, and object types. 2. MCX Crude Spread returned 0 results get_futures_sorted() hardcoded segments="FO" (NSE equity), but MCX commodity futures live in the "COMM" segment. Added a `segment` parameter to get_futures_sorted() (default "FO" for backward compatibility) and passed segment="COMM" for the MCX call. 3. Currency Futures Spread returned 0 results Same issue — currency futures use segments="CURR", not "FO". Passed segment="CURR" for both NSE and BSE fallback calls. Root cause in all cases: CLI scripts called search_instrument() directly with the correct segment, while the Streamlit app used the shared helper get_futures_sorted() which had the segment hardcoded to "FO". --- interactive_examples/streamlit_app.py | 28 ++++++++++++++++++--------- interactive_examples/utils.py | 6 +++++- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/interactive_examples/streamlit_app.py b/interactive_examples/streamlit_app.py index f33f144..9fc18f0 100644 --- a/interactive_examples/streamlit_app.py +++ b/interactive_examples/streamlit_app.py @@ -480,7 +480,7 @@ def contango_label(spread): if st.button("▶ Run", type="primary"): with st.spinner("Fetching MCX futures…"): - futures = get_futures_sorted(client, query, exchange="MCX", exact_symbol=False) + futures = get_futures_sorted(client, query, exchange="MCX", exact_symbol=False, segment="COMM") if len(futures) < 2: st.error(f"Need at least 2 futures for '{query}'. Try CRUDEOIL or NATURALGAS.") st.stop() @@ -1228,9 +1228,9 @@ def find_eq(exchange): if st.button("▶ Run", type="primary"): with st.spinner("Fetching currency futures…"): - futures = get_futures_sorted(client, pair, exchange="NSE", exact_symbol=True) + futures = get_futures_sorted(client, pair, exchange="NSE", exact_symbol=True, segment="CURR") if not futures: - futures = get_futures_sorted(client, pair, exchange="BSE", exact_symbol=True) + futures = get_futures_sorted(client, pair, exchange="BSE", exact_symbol=True, segment="CURR") if len(futures) < 2: st.error(f"Need at least 2 contracts for '{pair}'.") @@ -1822,9 +1822,16 @@ def session_label(exch, start_ms, end_ms): partial_notes = [] fully_open = set() for e in open_e: - exch = getattr(e, "exchange", e.get("exchange", "")) if not isinstance(e, str) else e - start_ms = getattr(e, "start_time", e.get("start_time", 0)) if not isinstance(e, str) else 0 - end_ms = getattr(e, "end_time", e.get("end_time", 0)) if not isinstance(e, str) else 0 + if isinstance(e, str): + exch, start_ms, end_ms = e, 0, 0 + elif isinstance(e, dict): + exch = e.get("exchange", "") + start_ms = e.get("start_time", 0) + end_ms = e.get("end_time", 0) + else: + exch = getattr(e, "exchange", "") + start_ms = getattr(e, "start_time", 0) + end_ms = getattr(e, "end_time", 0) label = session_label(exch, start_ms, end_ms) if "only" in label or "Muhurat" in label or "muhurat" in label: partial_notes.append(f"{exch}: {label}") @@ -1978,7 +1985,8 @@ def session_label(exch, start_ms, end_ms): elif example == "Live Depth (5-level)": client = require_client() - st.info("WebSocket streaming is not available in the web UI. This view polls the REST API instead.") + st.warning("⚠️ Live WebSocket streaming is not supported in the web UI. Please run this example locally via the command line.") + st.stop() SENSEX_IDX = "BSE_INDEX|SENSEX" NIFTY_IDX = "NSE_INDEX|Nifty 50" @@ -2057,7 +2065,8 @@ def render_depth(col, label, quote): elif example == "Live Depth MCX": client = require_client() - st.info("WebSocket streaming is not available in the web UI. This view polls the REST API instead.") + st.warning("⚠️ Live WebSocket streaming is not supported in the web UI. Please run this example locally via the command line.") + st.stop() MCX_QUERIES = [("GOLD", "MCX_FO"), ("SILVER", "MCX_FO"), ("CRUDEOIL", "MCX_FO"), ("NATURALGAS", "MCX_FO")] @@ -2117,7 +2126,8 @@ def _resolve_mcx(tok): elif example == "Live Depth USDINR": client = require_client() - st.info("WebSocket streaming is not available in the web UI. This view polls the REST API instead.") + st.warning("⚠️ Live WebSocket streaming is not supported in the web UI. Please run this example locally via the command line.") + st.stop() auto_refresh = st.checkbox("Auto-refresh (every 3s)", value=False) diff --git a/interactive_examples/utils.py b/interactive_examples/utils.py index 704416b..cf4d6b2 100644 --- a/interactive_examples/utils.py +++ b/interactive_examples/utils.py @@ -116,6 +116,7 @@ def get_futures_sorted( query: str, exchange: str = "NSE", exact_symbol: bool = False, + segment: str = "FO", ): """ Search for futures contracts and return them sorted by expiry (nearest first). @@ -124,6 +125,9 @@ def get_futures_sorted( matches *query* (case-insensitive) are returned — useful when searching 'NIFTY' to avoid picking up NIFTYNXT50, BANKNIFTY, etc. + Use segment="COMM" for MCX commodity futures (e.g. CRUDEOIL, NATURALGAS). + Use segment="FO" (default) for NSE/BSE equity futures. + Returns list of instrument dicts, each with keys like: instrument_key, trading_symbol, expiry, lot_size, underlying_symbol """ @@ -131,7 +135,7 @@ def get_futures_sorted( api_client, query, exchanges=exchange, - segments="FO", + segments=segment, instrument_types="FUT", records=30, ) From bb36f77bb799412c6967f32a8ca888e7f7d7e264 Mon Sep 17 00:00:00 2001 From: Shrini Date: Fri, 27 Mar 2026 10:23:10 +0400 Subject: [PATCH 2/3] Update live_depth_usdinr.py Added current month expiry --- interactive_examples/market_data/live_depth_usdinr.py | 1 - 1 file changed, 1 deletion(-) diff --git a/interactive_examples/market_data/live_depth_usdinr.py b/interactive_examples/market_data/live_depth_usdinr.py index 6deeb1a..024a11f 100644 --- a/interactive_examples/market_data/live_depth_usdinr.py +++ b/interactive_examples/market_data/live_depth_usdinr.py @@ -52,7 +52,6 @@ def find_usdinr(client, exchange: str): exchanges="NSE" if exchange == "CDS" else "BSE", segments="CURR", instrument_types="FUT", - expiry="current_month", records=10, ) instruments = resp.data or [] From cc149af47bc24d8a18fca330ef01a9cb2d028b1d Mon Sep 17 00:00:00 2001 From: PradeepJaiswar Date: Fri, 27 Mar 2026 15:23:35 +0530 Subject: [PATCH 3/3] Update live_depth_usdinr.py --- interactive_examples/market_data/live_depth_usdinr.py | 1 + 1 file changed, 1 insertion(+) diff --git a/interactive_examples/market_data/live_depth_usdinr.py b/interactive_examples/market_data/live_depth_usdinr.py index 024a11f..6deeb1a 100644 --- a/interactive_examples/market_data/live_depth_usdinr.py +++ b/interactive_examples/market_data/live_depth_usdinr.py @@ -52,6 +52,7 @@ def find_usdinr(client, exchange: str): exchanges="NSE" if exchange == "CDS" else "BSE", segments="CURR", instrument_types="FUT", + expiry="current_month", records=10, ) instruments = resp.data or []