eshan6704 commited on
Commit
74f6144
·
verified ·
1 Parent(s): eeba0bb

Delete build_nse_fno.py

Browse files
Files changed (1) hide show
  1. build_nse_fno.py +0 -261
build_nse_fno.py DELETED
@@ -1,261 +0,0 @@
1
- # fno.py
2
- import os
3
- import subprocess
4
- import zipfile
5
- import pandas as pd
6
- import datetime as dt
7
- import tempfile
8
- import pickle
9
-
10
- # ============================================================
11
- # CONFIG
12
- # ============================================================
13
- CACHE_DIR = "./cache/fno"
14
- os.makedirs(CACHE_DIR, exist_ok=True)
15
-
16
-
17
- # ============================================================
18
- # CACHE HELPERS (DATE-BASED)
19
- # ============================================================
20
- def _cache_path(key):
21
- return os.path.join(CACHE_DIR, f"{key}.pkl")
22
-
23
-
24
- def exists(key):
25
- return os.path.exists(_cache_path(key))
26
-
27
-
28
- def load(key):
29
- try:
30
- with open(_cache_path(key), "rb") as f:
31
- return pickle.load(f)
32
- except Exception:
33
- return None
34
-
35
-
36
- def save(key, obj):
37
- with open(_cache_path(key), "wb") as f:
38
- pickle.dump(obj, f)
39
-
40
-
41
- # ============================================================
42
- # FETCH FO BHAVCOPY (RAW)
43
- # ============================================================
44
- def fo_bhavcopy(date_input) -> pd.DataFrame:
45
- """
46
- Download NSE F&O bhavcopy for a given date
47
- date_input: dd-mm-yyyy | datetime.date | datetime.datetime
48
- """
49
- if isinstance(date_input, str):
50
- date = dt.datetime.strptime(date_input, "%d-%m-%Y").date()
51
- elif isinstance(date_input, dt.datetime):
52
- date = date_input.date()
53
- elif isinstance(date_input, dt.date):
54
- date = date_input
55
- else:
56
- raise ValueError("Invalid date format. Use dd-mm-yyyy")
57
-
58
- ymd = date.strftime("%Y%m%d")
59
- file_name = f"BhavCopy_NSE_FO_0_0_0_{ymd}_F_0000.csv"
60
- zip_name = f"{file_name}.zip"
61
- url = f"https://nsearchives.nseindia.com/content/fo/{zip_name}"
62
-
63
- with tempfile.TemporaryDirectory() as tmpdir:
64
- zip_path = os.path.join(tmpdir, zip_name)
65
-
66
- cmd = [
67
- "curl", "-L",
68
- "-A", "Mozilla/5.0",
69
- "--tlsv1.2",
70
- "--compressed",
71
- "-o", zip_path,
72
- url
73
- ]
74
-
75
- res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
76
-
77
- if res.returncode != 0 or not os.path.exists(zip_path) or os.path.getsize(zip_path) < 1024:
78
- raise RuntimeError("FO Bhavcopy download failed or blocked")
79
-
80
- with zipfile.ZipFile(zip_path) as z:
81
- with z.open(file_name) as f:
82
- df = pd.read_csv(f)
83
-
84
- return df
85
-
86
-
87
- # ============================================================
88
- # OPTION CHAIN BUILDER
89
- # ============================================================
90
- def build_option_chain(opt_df: pd.DataFrame) -> pd.DataFrame:
91
- drop = [
92
- "FininstrmActlXpryDt", "FinInstrmTp", "TckrSymb",
93
- "TtlNbOfTxsExctd", "NewBrdLotQty",
94
- "EXP_DMY", "SttlmPric",
95
- "OpnPric", "HghPric", "LwPric", "TtlTrfVal"
96
- ]
97
-
98
- rename = {
99
- "ClsPric": "close",
100
- "PrvsClsgPric": "pre",
101
- "OpnIntrst": "oi",
102
- "ChngInOpnIntrst": "oi_chg",
103
- "TtlTradgVol": "vol"
104
- }
105
-
106
- opt_df = opt_df.drop(drop, axis=1, errors="ignore").rename(columns=rename)
107
-
108
- ce = opt_df[opt_df["OptnTp"] == "CE"].rename(
109
- columns={c: f"ce_{c}" for c in opt_df.columns}
110
- )
111
- pe = opt_df[opt_df["OptnTp"] == "PE"].rename(
112
- columns={c: f"pe_{c}" for c in opt_df.columns}
113
- )
114
-
115
- chain = pd.merge(
116
- ce, pe,
117
- left_on="ce_StrkPric",
118
- right_on="pe_StrkPric",
119
- how="outer"
120
- )
121
-
122
- chain["StrkPric"] = chain["ce_StrkPric"].combine_first(chain["pe_StrkPric"])
123
-
124
- chain.drop(
125
- columns=[
126
- "ce_StrkPric", "pe_StrkPric",
127
- "ce_OptnTp", "pe_OptnTp",
128
- "ce_UndrlygPric", "pe_UndrlygPric"
129
- ],
130
- inplace=True,
131
- errors="ignore"
132
- )
133
-
134
- chain = chain.fillna(0).sort_values("StrkPric").reset_index(drop=True)
135
-
136
- cols = [
137
- "ce_oi", "ce_oi_chg", "ce_vol", "ce_close", "ce_pre",
138
- "StrkPric",
139
- "pe_pre", "pe_close", "pe_vol", "pe_oi_chg", "pe_oi"
140
- ]
141
-
142
- df = chain[cols].copy()
143
-
144
- for c in ["ce_close", "ce_pre", "pe_close", "pe_pre"]:
145
- df[c] = df[c].astype(float).round(2)
146
-
147
- for c in [
148
- "ce_oi", "ce_oi_chg", "ce_vol",
149
- "pe_vol", "pe_oi_chg", "pe_oi", "StrkPric"
150
- ]:
151
- df[c] = df[c].astype(int)
152
-
153
- return df
154
-
155
-
156
- # ============================================================
157
- # HTML TABLE RENDER
158
- # ============================================================
159
- def df_to_html(df: pd.DataFrame, title=None) -> str:
160
- style = """
161
- <style>
162
- table {border-collapse: collapse; width:100%; font-family:Arial;}
163
- th, td {border:1px solid #ddd; padding:6px; text-align:center;}
164
- th {background:#2e7d32; color:white;}
165
- tr:nth-child(even){background:#f2f2f2;}
166
- </style>
167
- """
168
-
169
- html = df.to_html(index=False, escape=False)
170
- if title:
171
- html = f"<h3>{title}</h3>" + html
172
-
173
- return style + html
174
-
175
-
176
- # ============================================================
177
- # MAIN ENTRY (DAILY VALIDITY)
178
- # ============================================================
179
- def nse_fno_html(fo_date: str, symbol: str) -> str:
180
- """
181
- Daily-valid F&O HTML builder
182
- Cache rules:
183
- - HTML cached per (date + symbol)
184
- - FO bhavcopy cached per date
185
- """
186
-
187
- date_key = dt.datetime.strptime(fo_date, "%d-%m-%Y").strftime("%Y%m%d")
188
-
189
- html_key = f"fno_html_{date_key}_{symbol}"
190
- fo_key = f"fno_bhavcopy_{date_key}"
191
-
192
- # ---------------- HTML CACHE FIRST ----------------
193
- if exists(html_key):
194
- html = load(html_key)
195
- if html:
196
- return html
197
-
198
- # ---------------- FO CACHE ----------------
199
- if exists(fo_key):
200
- fo_df = load(fo_key)
201
- else:
202
- fo_df = fo_bhavcopy(fo_date)
203
- save(fo_key, fo_df)
204
-
205
- # ---------------- BUILD DATA ----------------
206
- fo = fo_df.copy().drop(
207
- ["ISIN", "Rmks", "SctySrs", "Rsvd1", "Rsvd2", "Rsvd3", "Rsvd4"],
208
- axis=1,
209
- errors="ignore"
210
- )
211
-
212
- exp = pd.to_datetime(fo["FininstrmActlXpryDt"], errors="coerce")
213
- today = pd.Timestamp.today().normalize()
214
-
215
- monthly = (
216
- exp[exp >= today]
217
- .groupby([exp.dt.year, exp.dt.month])
218
- .max()
219
- .sort_values()
220
- )
221
-
222
- if monthly.empty:
223
- return "<h3>No valid expiry found</h3>"
224
-
225
- expiry = monthly.iloc[0].strftime("%d-%m-%Y")
226
-
227
- fo["EXP_DMY"] = exp.dt.strftime("%d-%m-%Y")
228
-
229
- df = fo[
230
- (fo["TckrSymb"] == symbol) &
231
- (fo["EXP_DMY"] == expiry)
232
- ].copy()
233
-
234
- if df.empty:
235
- return f"<h3>No F&O data for {symbol}</h3>"
236
-
237
- # ---------------- COMMON ----------------
238
- common_cols = [
239
- "TradDt", "BizDt", "Sgmt", "Src", "SsnId",
240
- "FinInstrmId", "XpryDt", "FinInstrmNm", "LastPric"
241
- ]
242
-
243
- common_df = pd.DataFrame([df.iloc[0][common_cols]])
244
- common_df.insert(0, "Expiry", expiry)
245
-
246
- # ---------------- FUTURE + OPTION ----------------
247
- future_df = df[df["FinInstrmTp"].isin(["STF", "IDF"])]
248
- option_df = df[df["FinInstrmTp"].isin(["STO", "IDO"])]
249
-
250
- option_chain_df = build_option_chain(option_df)
251
-
252
- html = (
253
- df_to_html(common_df, "Common Info") + "<br>"
254
- + df_to_html(future_df, "Future Contracts") + "<br>"
255
- + df_to_html(option_chain_df, "Option Chain")
256
- )
257
-
258
- # ---------------- SAVE HTML ----------------
259
- save(html_key, html)
260
-
261
- return html