eshan6704 commited on
Commit
a3918fc
·
verified ·
1 Parent(s): 0bb4ac0

Update nse.py

Browse files
Files changed (1) hide show
  1. nse.py +174 -343
nse.py CHANGED
@@ -1,395 +1,226 @@
1
- # ================================
2
- # NSE Fetch Module (DF Only)
3
- # ================================
4
-
5
- import datetime
6
- import pandas as pd
7
- import time
8
  import requests
9
- import nsepython # Moved import here
10
- #import nse
11
- HEADERS = {
12
- "User-Agent": "Mozilla/5.0",
 
 
 
13
  "Accept-Language": "en-US,en;q=0.9",
14
  }
15
 
16
- session = requests.Session()
17
- session.get("https://www.nseindia.com", headers=HEADERS, timeout=5)
18
 
19
- # ---------------------------------------------------
20
- # Helper: JSON Fetch
21
- # ---------------------------------------------------
22
- def fetch_data(url):
23
- try:
24
- response = session.get(url, headers=HEADERS, timeout=5)
25
- response.raise_for_status()
26
- return response.json()
27
- except:
28
- return None
29
-
30
- # ---------------------------------------------------
31
- # Clean DF
32
- # ---------------------------------------------------
33
- def clean_dataframe(df):
34
- df.columns = df.columns.str.strip()
35
- str_cols = df.select_dtypes(include=["object"]).columns
36
- df[str_cols] = df[str_cols].apply(lambda x: x.str.strip())
37
- df.fillna(0.01, inplace=True)
38
- return df
39
-
40
- # ---------------------------------------------------
41
- # Bhavcopy Fetch → DataFrame
42
- # ---------------------------------------------------
43
- def nse_bhavcopy(date):
44
- """Returns Cleaned Bhavcopy DF for EQ / BE / SM"""
45
- date_str = date.strftime("%d-%m-%Y")
46
- print(f"Attempting to fetch bhavcopy for date: {date_str}")
47
 
 
 
 
 
 
48
  try:
49
- df = nsepython.get_bhavcopy(date_str) # Direct call
50
- if df is None or df.empty:
51
- print(f"No bhavcopy data or empty DataFrame returned for {date_str}")
52
- return None, None
53
 
54
- actual_bhavcopy_date = datetime.datetime.strptime(
55
- df.iloc[2, 2].strip(), "%d-%b-%Y"
56
- ).date()
57
 
58
- df = clean_dataframe(df)
59
- df_filtered = df[df.iloc[:, 1].isin(["EQ", "BE", "SM"])]
 
 
60
 
61
- return df_filtered, actual_bhavcopy_date
62
 
63
  except Exception as e:
64
- print(f"An error occurred while fetching bhavcopy for {date_str}: {e}")
65
- return None, None
66
-
67
- # ---------------------------------------------------
68
- # Stock Deliverable DF (security-wise archive)
69
- # ---------------------------------------------------
70
- def nse_stock(nse_module, stock, start, end, series="ALL"):
71
- """Return DF for security-wise archive (deliverable + all columns)"""
72
-
73
- df = nse_module.security_wise_archive(start, end, stock, series)
74
- if df is not None and not df.empty:
75
- return df
76
- return None
77
-
78
- # ---------------------------------------------------
79
- # All NSE Indices → DataFrames
80
- # ---------------------------------------------------
81
- def nse_indices():
82
- url = "https://www.nseindia.com/api/allIndices"
83
- data = fetch_data(url)
84
- if data is None:
85
- return None
86
-
87
- # DataFrames
88
- df_dates = pd.DataFrame([data["dates"]])
89
- df_meta = pd.DataFrame([{k: v for k, v in data.items() if k not in ["data", "dates"]}])
90
- df_data = pd.DataFrame(data["data"])
91
-
92
- # Convert to HTML pieces
93
- html_dates = df_dates.to_html(index=False, border=1)
94
- html_meta = df_meta.to_html(index=False, border=1)
95
- html_data = df_data.to_html(index=False, border=1)
96
-
97
- # Combine into one single HTML block
98
- full_html = (
99
- "<h3>Dates</h3>" + html_dates +
100
- "<br><h3>Meta</h3>" + html_meta +
101
- "<br><h3>Data</h3>" + html_data
102
- )
103
 
104
- return full_html
105
 
106
- # ---------------------------------------------------
107
- # Specific Index DataFrames
108
- # ---------------------------------------------------
109
- '''
110
- def open(index_name="NIFTY 50"):
111
- url = f"https://www.nseindia.com/api/equity-stockIndices?index={index_name.replace(' ', '%20')}"
112
- data = fetch_data(url)
113
- if data is None:
114
- return None
115
-
116
- # Create DataFrames
117
- df_market = pd.DataFrame([data["marketStatus"]])
118
- df_adv = pd.DataFrame([data["advance"]])
119
- df_meta = pd.DataFrame([data["metadata"]])
120
- df_data = pd.DataFrame(data["data"])
121
-
122
- # Convert to HTML
123
- html_market = df_market.to_html(index=False, border=1)
124
- html_adv = df_adv.to_html(index=False, border=1)
125
- html_meta = df_meta.to_html(index=False, border=1)
126
- html_data = df_data.to_html(index=False, border=1)
127
-
128
- # Combine all into single HTML string
129
- full_html = (
130
- "<h3>Market Status</h3>" + html_market +
131
- "<br><h3>Advance / Decline</h3>" + html_adv +
132
- "<br><h3>Metadata</h3>" + html_meta +
133
- "<br><h3>Index Data</h3>" + html_data
134
- )
135
 
136
- return full_html
137
 
138
- '''
139
- import requests
140
- import json
 
 
 
 
 
 
 
 
141
 
142
- def fetch_data_from_nse(url):
143
- headers = {"User-Agent": "Mozilla/5.0"}
 
 
 
 
 
 
 
 
 
144
  try:
145
- resp = requests.get(url, headers=headers)
146
- if resp.status_code == 200:
147
- return resp.json()
148
- except:
149
- return None
150
-
151
- # ---------------------------------------------------
152
- # Option Chain DF (Raw CE/PE)
153
- # ---------------------------------------------------
154
- def fetch_option_chain_df(symbol="NIFTY"):
155
- url = f"https://www.nseindia.com/api/option-chain-indices?symbol={symbol}"
156
- data = fetch_data(url)
157
-
158
- if data and "filtered" in data:
159
- ce_df = pd.DataFrame([r["CE"] for r in data["filtered"]["data"] if "CE" in r])
160
- pe_df = pd.DataFrame([r["PE"] for r in data["filtered"]["data"] if "PE" in r])
161
- return ce_df, pe_df
162
-
163
- return None, None
164
-
165
- # ---------------------------------------------------
166
- # Pre-open market → DataFrame
167
- # ---------------------------------------------------
168
- def nse_preopen(key="NIFTY"):
169
- url = f"https://www.nseindia.com/api/market-data-pre-open?key={key}"
170
- data = fetch_data(url)
171
- if not data:
172
- return None
173
-
174
- df = pd.DataFrame(data.get("data", []))
175
-
176
- # Convert to one HTML table
177
- html_table = df.to_html(index=False, border=1)
178
-
179
- # Wrap into single block
180
- full_html = "<h3>Pre-Open Market Data</h3>" + html_table
181
-
182
- return full_html
183
-
184
- # ---------------------------------------------------
185
- # FNO Quote → DataFrames
186
- # ---------------------------------------------------
187
- def nse_fno(symbol):
188
- payload = nsepython.nse_quote(symbol)
189
- if not payload:
190
- return None
191
-
192
- # ---------- INFO ----------
193
- info_keys = list(payload["info"].keys()) + [
194
- "fut_timestamp",
195
- "opt_timestamp",
196
- "maxVolatility",
197
- "minVolatility",
198
- "avgVolatility",
199
- ]
200
-
201
- info_values = list(payload["info"].values()) + [
202
- payload["fut_timestamp"],
203
- payload["opt_timestamp"],
204
- payload["underlyingInfo"]["volatility"][0]['maxVolatility'],
205
- payload["underlyingInfo"]["volatility"][0]['minVolatility'],
206
- payload["underlyingInfo"]["volatility"][0]['avgVolatility'],
207
- ]
208
-
209
- df_info = pd.DataFrame([info_values], columns=info_keys)
210
-
211
- # ---------- MCAP ----------
212
- df_mcap = pd.DataFrame(payload["underlyingInfo"].get("marketCap", {}))
213
-
214
- # ---------- FNO LIST ----------
215
- df_fno_list = pd.DataFrame(payload.get("allSymbol", []), columns=["FNO_Symbol"])
216
-
217
- # ---------- STOCK DEPTH ----------
218
- df_stock = process_stocks_df(payload["stocks"])
219
-
220
- # Convert all to HTML
221
- html_info = df_info.to_html(index=False, border=1)
222
- html_mcap = df_mcap.to_html(index=False, border=1)
223
- html_fno = df_fno_list.to_html(index=False, border=1)
224
- html_stock = df_stock.to_html(index=False, border=1)
225
-
226
- # Combine into full HTML block
227
- full_html = (
228
- "<h3>FNO Info</h3>" + html_info +
229
- "<br><h3>Market Cap</h3>" + html_mcap +
230
- "<br><h3>FNO Symbol List</h3>" + html_fno +
231
- "<br><h3>Stock Depth</h3>" + html_stock
232
- )
233
 
234
- return full_html
 
235
 
236
- # ---------------------------------------------------
237
- # Handle nested stock → DF
238
- # ---------------------------------------------------
239
- def process_stocks_df(data):
240
- """Return final merged stock DF only"""
241
- trade_info_list, other_info_list = [], []
242
- bid_ask_list = []
243
- stock_values = []
244
- trade_keys = other_keys = bidask_keys = stock_keys = None
245
 
246
- for i, stock in enumerate(data):
247
- meta = stock.pop("metadata")
248
- depth = stock.pop("marketDeptOrderBook")
249
- parent = stock
250
 
251
- trade_info = depth.pop("tradeInfo", {})
252
- other_info = depth.pop("otherInfo", {})
253
 
254
- trade_info_list.append(trade_info)
255
- other_info_list.append(other_info)
256
 
257
- # bid / ask
258
- bid_ask_row = {}
259
- for side in ["bid", "ask"]:
260
- for j, entry in enumerate(depth.get(side, []), start=1):
261
- bid_ask_row[f"{side}_price_{j}"] = entry.get("price")
262
- bid_ask_row[f"{side}_qty_{j}"] = entry.get("quantity")
 
 
263
 
264
- bid_ask_list.append(bid_ask_row)
 
265
 
266
- if i == 0:
267
- trade_keys = list(trade_info.keys())
268
- other_keys = list(other_info.keys())
269
- bidask_keys = list(bid_ask_row.keys())
270
- stock_keys = list(meta.keys()) + list(depth.keys()) + list(parent.keys())
271
 
272
- stock_values.append(
273
- list(meta.values()) + list(depth.values()) + list(parent.values())
274
- )
275
 
276
- df_trade = pd.DataFrame(trade_info_list, columns=trade_keys)
277
- df_other = pd.DataFrame(other_info_list, columns=other_keys)
278
- df_bidask = pd.DataFrame(bid_ask_list, columns=bidask_keys)
279
- df_stock = pd.DataFrame(stock_values, columns=stock_keys)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
 
281
- df_stock = df_stock.drop(columns=['bid', 'ask', 'carryOfCost'], errors="ignore")
 
 
282
 
283
- return pd.concat([df_stock, df_trade, df_other, df_bidask], axis=1)
 
284
 
 
 
285
 
 
 
 
 
 
 
 
286
 
 
 
 
287
 
288
- #date = datetime.date(2025, 11, 27) # Trying a past date where data is likely available
289
 
290
- #df = nse_preopen_df("NIFTY")
291
- #df_bhav, act_date = fetch_bhavcopy_df(date)
292
- #df_ce, df_pe = fetch_option_chain_df("NIFTY")
293
- #df_m, df_a, df_meta, df_data = nse_index_df("NIFTY 50")
294
 
295
- #fno = nse_fno_df("RELIANCE")
 
296
 
297
 
298
- # -----------------------------
299
- # Global Variables
300
- # -----------------------------
301
- nse_del_key_map = {
302
- 'Symbol': "Symbol", 'Series': "Series",
303
- 'Date': 'Date', 'Prev Close': 'Preclose',
304
- 'Open Price': 'Open', 'High Price': 'High',
305
- 'Low Price': 'Low', 'Last Price': 'Last',
306
- 'Close Price': 'Close', 'Average Price': 'AvgPrice',
307
- 'Total Traded Quantity': 'Volume',
308
- 'Turnover ₹': 'Turnover', 'No. of Trades': "Trades",
309
- 'Deliverable Qty': "Delivery", '% Dly Qt to Traded Qty': "Del%"
310
- }
 
 
311
 
312
- # -----------------------------
313
- # Data Fetching Functions (NSE)
314
- # -----------------------------
315
- def url_nse_del(symbol, start_date, end_date):
316
- base_url = "https://www.nseindia.com/api/historicalOR/generateSecurityWiseHistoricalData"
317
- start_date_str = start_date.strftime("%d-%m-%Y")
318
- end_date_str = end_date.strftime("%d-%m-%Y")
319
- url = f"{base_url}?from={start_date_str}&to={end_date_str}&symbol={symbol.split('.')[0]}&type=priceVolumeDeliverable&series=ALL&csv=true"
320
- return url
321
-
322
- def to_numeric_safe(series):
323
- series = series.replace('-', 0)
324
- series = series.fillna(0)
325
- series = series.astype(str).str.replace(',', '')
326
- return pd.to_numeric(series, errors='coerce').fillna(0)
327
-
328
- def nse_daily(symbol, start_date_str=None, end_date_str=None):
329
- # Default end date is today
330
- end_date = datetime.now()
331
- if end_date_str:
332
- try:
333
- end_date = datetime.strptime(end_date_str, "%Y-%m-%d")
334
- except ValueError:
335
- print(f"Warning: Invalid end date format '{end_date_str}'. Using today's date.")
336
- end_date = datetime.now()
337
-
338
- # Default start date is one year prior
339
- start_date = end_date - timedelta(days=365)
340
- if start_date_str:
341
- try:
342
- start_date = datetime.strptime(start_date_str, "%Y-%m-%d")
343
- except ValueError:
344
- print(f"Warning: Invalid start date format '{start_date_str}'. Using default start date.")
345
- start_date = end_date - timedelta(days=365)
346
-
347
- # Swap if needed
348
- if start_date > end_date:
349
- print("Warning: Start date is after end date. Swapping dates.")
350
- start_date, end_date = end_date, start_date
351
-
352
- url = url_nse_del(symbol, start_date, end_date)
353
- headers = {"User-Agent": "Mozilla/5.0"}
354
 
 
 
 
 
 
355
  try:
356
- response = requests.get(url, headers=headers)
357
- response.raise_for_status()
358
 
359
- if not response.content:
360
- return None
361
 
362
- # Build DataFrame
363
- df = pd.read_csv(io.StringIO(response.content.decode("utf-8"))).round(2)
364
- df.columns = df.columns.str.strip()
365
- df.rename(columns=nse_del_key_map, inplace=True)
366
 
367
- # Capitalize column names
368
- df.columns = [col.capitalize() for col in df.columns]
369
 
370
- # Remove unwanted columns
371
- df.drop(columns=["Symbol", "Series", "Avgprice", "Last"], errors="ignore", inplace=True)
372
 
373
- # Format date
374
- df["Date"] = pd.to_datetime(df["Date"], format="%d-%b-%Y").dt.strftime("%Y-%m-%d")
375
 
376
- # Ensure numeric columns
377
- numeric_cols = ['Close', 'Preclose', 'Open', 'High', 'Low', 'Volume', 'Delivery', 'Turnover', 'Trades']
378
- numeric_cols_cap = [c.capitalize() for c in numeric_cols]
 
 
 
 
 
379
 
380
- for col in numeric_cols_cap:
381
- if col in df.columns:
382
- df[col] = to_numeric_safe(df[col])
383
- else:
384
- df[col] = 0
385
 
386
- # Convert to HTML
387
- html_table = df.to_html(index=False, border=1)
 
388
 
389
- full_html = "<h3>Daily Data</h3>" + html_table
390
- return full_html
391
 
392
  except Exception as e:
393
- print(f"Error fetching data from NSE for {symbol}: {e}")
394
- return None
395
-
 
 
 
 
 
 
 
 
1
  import requests
2
+ import pandas as pd
3
+
4
+ # ======================================================
5
+ # Base Headers (mandatory for NSE API)
6
+ # ======================================================
7
+ NSE_HEADERS = {
8
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
9
  "Accept-Language": "en-US,en;q=0.9",
10
  }
11
 
 
 
12
 
13
+ # ======================================================
14
+ # Helper: Convert DataFrame to HTML (single block)
15
+ # ======================================================
16
+ def _to_html(title, df):
17
+ if df is None or len(df) == 0:
18
+ return f"<h3>{title}</h3><p>No data available</p>"
19
+ return f"<h3>{title}</h3>" + df.to_html(index=False, border=1)
20
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
+ # ======================================================================
23
+ # STOCK QUOTE (similar to get_quote)
24
+ # ======================================================================
25
+ def nse_stock(symbol):
26
+ url = f"https://www.nseindia.com/api/quote-equity?symbol={symbol.upper()}"
27
  try:
28
+ r = requests.get(url, headers=NSE_HEADERS)
29
+ data = r.json()
 
 
30
 
31
+ info = pd.json_normalize(data.get("info", {}))
32
+ price = pd.json_normalize(data.get("priceInfo", {}))
33
+ meta = pd.json_normalize(data.get("metadata", {}))
34
 
35
+ html = ""
36
+ html += _to_html("Stock Info", info)
37
+ html += _to_html("Price Info", price)
38
+ html += _to_html("Metadata", meta)
39
 
40
+ return html
41
 
42
  except Exception as e:
43
+ return f"<p>Error fetching stock: {e}</p>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
 
45
 
46
+ # ======================================================================
47
+ # OPTION CHAIN (similar to get_option_chain)
48
+ # ======================================================================
49
+ def nse_fno(symbol):
50
+ url = f"https://www.nseindia.com/api/option-chain-equities?symbol={symbol.upper()}"
51
+ try:
52
+ r = requests.get(url, headers=NSE_HEADERS)
53
+ data = r.json()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
+ records = data.get("records", {})
56
 
57
+ all_data = pd.DataFrame(records.get("data", []))
58
+ ce_list = [x["CE"] for x in records.get("data", []) if "CE" in x]
59
+ pe_list = [x["PE"] for x in records.get("data", []) if "PE" in x]
60
+
61
+ df_ce = pd.DataFrame(ce_list)
62
+ df_pe = pd.DataFrame(pe_list)
63
+
64
+ html = ""
65
+ html += _to_html("F&O Combined", all_data)
66
+ html += _to_html("CALL Options (CE)", df_ce)
67
+ html += _to_html("PUT Options (PE)", df_pe)
68
 
69
+ return html
70
+
71
+ except Exception as e:
72
+ return f"<p>Error fetching FNO: {e}</p>"
73
+
74
+
75
+ # ======================================================================
76
+ # FUTURES DATA
77
+ # ======================================================================
78
+ def nse_future(symbol):
79
+ url = f"https://www.nseindia.com/api/quote-derivative?symbol={symbol.upper()}"
80
  try:
81
+ r = requests.get(url, headers=NSE_HEADERS)
82
+ data = r.json()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
 
84
+ futures = pd.DataFrame(data.get("stocks", []))
85
+ meta = pd.json_normalize(data.get("info", {}))
86
 
87
+ html = ""
88
+ html += _to_html("Futures Data", futures)
89
+ html += _to_html("Metadata", meta)
 
 
 
 
 
 
90
 
91
+ return html
 
 
 
92
 
93
+ except Exception as e:
94
+ return f"<p>Error fetching futures: {e}</p>"
95
 
 
 
96
 
97
+ # ======================================================================
98
+ # 52-WEEK HIGH / LOW
99
+ # ======================================================================
100
+ def nse_high_low():
101
+ url = "https://www.nseindia.com/api/market-data-52Week"
102
+ try:
103
+ r = requests.get(url, headers=NSE_HEADERS)
104
+ data = r.json()
105
 
106
+ high = pd.DataFrame(data.get("FiftyTwoWeekHigh", []))
107
+ low = pd.DataFrame(data.get("FiftyTwoWeekLow", []))
108
 
109
+ html = ""
110
+ html += _to_html("52 Week High", high)
111
+ html += _to_html("52 Week Low", low)
 
 
112
 
113
+ return html
 
 
114
 
115
+ except Exception as e:
116
+ return f"<p>Error fetching high-low: {e}</p>"
117
+
118
+
119
+ # ======================================================================
120
+ # BHAVCOPY
121
+ # ======================================================================
122
+ def nse_bhav(date_str):
123
+ """
124
+ Supports input date format:
125
+ - DDMMYYYY
126
+ - DD-MM-YYYY
127
+ - DD/MM/YYYY
128
+ Converts automatically to DDMMYYYY for NSE API.
129
+ """
130
+
131
+ # ---------------------------
132
+ # Normalize date
133
+ # ---------------------------
134
+ try:
135
+ # Replace "-" or "/" with ""
136
+ clean = date_str.replace("-", "").replace("/", "")
137
 
138
+ # Now clean should be DDMMYYYY
139
+ if len(clean) != 8:
140
+ return "<p>Error: Invalid date format. Use DDMMYYYY / DD-MM-YYYY / DD/MM/YYYY</p>"
141
 
142
+ # Final API format = DDMMYYYY
143
+ api_date = clean
144
 
145
+ except Exception:
146
+ return "<p>Error: Unable to parse date.</p>"
147
 
148
+ # ---------------------------
149
+ # Fetch Data
150
+ # ---------------------------
151
+ url = (
152
+ "https://www.nseindia.com/api/reports"
153
+ f"?archives=true&date={api_date}&type=equities&mode=single"
154
+ )
155
 
156
+ try:
157
+ r = requests.get(url, headers=NSE_HEADERS)
158
+ data = r.json()
159
 
160
+ df = pd.DataFrame(data.get("data", []))
161
 
162
+ return _to_html(f"Bhavcopy {api_date}", df)
 
 
 
163
 
164
+ except Exception as e:
165
+ return f"<p>Error fetching bhavcopy: {e}</p>"
166
 
167
 
168
+ # ======================================================================
169
+ # ALL NSE INDICES LIST
170
+ # ======================================================================
171
+ def nse_indices():
172
+ url = "https://www.nseindia.com/api/allIndices"
173
+ try:
174
+ r = requests.get(url, headers=NSE_HEADERS)
175
+ data = r.json()
176
+
177
+ df = pd.DataFrame(data.get("data", []))
178
+
179
+ return _to_html("All NSE Indices", df)
180
+
181
+ except Exception as e:
182
+ return f"<p>Error fetching indices: {e}</p>"
183
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
 
185
+ # ======================================================================
186
+ # NSE OPEN MARKET DATA (same endpoint used in nsepython)
187
+ # ======================================================================
188
+ def nse_open(index_name):
189
+ url = f"https://www.nseindia.com/api/equity-stockIndices?index={index_name.replace(' ', '%20')}"
190
  try:
191
+ r = requests.get(url, headers=NSE_HEADERS)
192
+ data = r.json()
193
 
194
+ meta = pd.json_normalize(data.get("metadata", {}))
195
+ df = pd.DataFrame(data.get("data", []))
196
 
197
+ html = ""
198
+ html += _to_html("Index Metadata", meta)
199
+ html += _to_html("Index Open Data", df)
 
200
 
201
+ return html
 
202
 
203
+ except Exception as e:
204
+ return f"<p>Error fetching open data: {e}</p>"
205
 
 
 
206
 
207
+ # ======================================================================
208
+ # NSE PRE-OPEN MARKET DATA
209
+ # ======================================================================
210
+ def nse_preopen(index_name):
211
+ url = "https://www.nseindia.com/api/market-data-pre-open?key=NIFTY"
212
+ try:
213
+ r = requests.get(url, headers=NSE_HEADERS)
214
+ data = r.json()
215
 
216
+ df = pd.DataFrame(data.get("data", []))
217
+ meta = pd.json_normalize(data.get("metadata", {}))
 
 
 
218
 
219
+ html = ""
220
+ html += _to_html("Pre-Open Metadata", meta)
221
+ html += _to_html("Pre-Open Market Data", df)
222
 
223
+ return html
 
224
 
225
  except Exception as e:
226
+ return f"<p>Error fetching preopen: {e}</p>"