eshan6704 commited on
Commit
2ba9451
·
verified ·
1 Parent(s): fbdc840

Create preopen_html.py

Browse files
Files changed (1) hide show
  1. app/preopen_html.py +175 -0
app/preopen_html.py ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from nsepython import *
2
+ import pandas as pd
3
+ import re
4
+ from datetime import datetime as dt
5
+
6
+ # persist helpers (HF only)
7
+ from persist import exists, load, save
8
+
9
+
10
+ def build_preopen_html(key="NIFTY"):
11
+ """
12
+ Build full Pre-Open HTML
13
+ - Daily TTL (via persist.py)
14
+ - HTML only cache
15
+ """
16
+
17
+ # ================= CACHE (TTL via persist) =================
18
+ cache_name = f"DAILY_PREOPEN_{key.upper()}"
19
+
20
+ if exists(cache_name, "html"):
21
+ cached_html = load(cache_name, "html")
22
+ if isinstance(cached_html, str):
23
+ return cached_html
24
+
25
+ # ================= FETCH DATA =================
26
+ p = nsefetch(f"https://www.nseindia.com/api/market-data-pre-open?key={key}")
27
+
28
+ data_df = df_from_data(p.pop("data"))
29
+ rem_df = df_from_data([p])
30
+
31
+ main_df = data_df.iloc[[0]] if not data_df.empty else pd.DataFrame()
32
+ const_df = data_df.iloc[1:] if len(data_df) > 1 else pd.DataFrame()
33
+
34
+ # ================= REMOVE PATTERN COLUMNS =================
35
+ pattern_remove = re.compile(r"^(price_|buyQty_|sellQty_|iep_)\d+$")
36
+
37
+ def remove_pattern_cols(df):
38
+ if df is None or df.empty:
39
+ return df
40
+ return df[[c for c in df.columns if not pattern_remove.match(c)]]
41
+
42
+ main_df = remove_pattern_cols(main_df)
43
+ const_df = remove_pattern_cols(const_df)
44
+ rem_df = remove_pattern_cols(rem_df)
45
+
46
+ # ================= TABLE COLOR HELPER =================
47
+ def df_to_html_color(df, metric_col=None):
48
+ if df is None or df.empty:
49
+ return "<i>No data</i>"
50
+
51
+ df_html = df.copy()
52
+ top_up, top_down = [], []
53
+
54
+ if metric_col and metric_col in df_html.columns:
55
+ col_num = pd.to_numeric(df_html[metric_col], errors="coerce").dropna()
56
+ top_up = col_num.nlargest(3).index.tolist()
57
+ top_down = col_num.nsmallest(3).index.tolist()
58
+
59
+ for idx, row in df_html.iterrows():
60
+ for col in df_html.columns:
61
+ val = row[col]
62
+ cls = ""
63
+ if isinstance(val, (int, float)):
64
+ val_fmt = f"{val:.2f}"
65
+ if val > 0:
66
+ cls = "numeric-positive"
67
+ elif val < 0:
68
+ cls = "numeric-negative"
69
+ if metric_col and col == metric_col:
70
+ if idx in top_up:
71
+ cls += " top-up"
72
+ elif idx in top_down:
73
+ cls += " top-down"
74
+ df_html.at[idx, col] = f'<span class="{cls.strip()}">{val_fmt}</span>'
75
+ else:
76
+ df_html.at[idx, col] = str(val)
77
+
78
+ return df_html.to_html(index=False, escape=False, classes="compact-table")
79
+
80
+ # ================= MINI INFO CARDS =================
81
+ def build_info_cards(rem_df, main_df):
82
+ combined = pd.concat([rem_df, main_df], axis=1)
83
+ combined = combined.loc[:, ~combined.columns.duplicated()]
84
+ combined = remove_pattern_cols(combined)
85
+
86
+ html = '<div class="mini-card-container">'
87
+ for col in combined.columns:
88
+ val = combined.at[0, col] if not combined.empty else ""
89
+ html += f"""
90
+ <div class="mini-card">
91
+ <div class="card-key">{col}</div>
92
+ <div class="card-val">{val}</div>
93
+ </div>
94
+ """
95
+ html += '</div>'
96
+ return html
97
+
98
+ info_cards_html = build_info_cards(rem_df, main_df)
99
+
100
+ # ================= CONSTITUENTS TABLE =================
101
+ cons_html = df_to_html_color(const_df)
102
+
103
+ # ================= METRIC TABLES =================
104
+ metric_cols_allowed = [
105
+ "pChange",
106
+ "totalTurnover",
107
+ "marketCap",
108
+ "totalTradedVolume"
109
+ ]
110
+
111
+ metric_tables = ""
112
+ for col in metric_cols_allowed:
113
+ if col in const_df.columns:
114
+ df_m = const_df.copy()
115
+ df_m[col] = pd.to_numeric(df_m[col], errors="coerce")
116
+ df_m = df_m.sort_values(col, ascending=False)
117
+
118
+ show_cols = ["symbol", col] if "symbol" in df_m.columns else [col]
119
+ metric_tables += f"""
120
+ <div class="small-table">
121
+ <div class="st-title">{col}</div>
122
+ <div class="st-body">
123
+ {df_to_html_color(df_m[show_cols], metric_col=col)}
124
+ </div>
125
+ </div>
126
+ """
127
+
128
+ # ================= FINAL HTML =================
129
+ html_out = f"""
130
+ <!DOCTYPE html>
131
+ <html>
132
+ <head>
133
+ <meta charset="UTF-8">
134
+ <style>
135
+ body {{ font-family: Arial; margin: 12px; background: #f5f5f5; font-size: 14px; }}
136
+ h2, h3 {{ margin: 10px 0; }}
137
+ table {{ border-collapse: collapse; width: 100%; }}
138
+ th, td {{ border: 1px solid #bbb; padding: 6px; font-size: 13px; }}
139
+ th {{ background: #333; color: #fff; }}
140
+ .numeric-positive {{ color: green; font-weight: bold; }}
141
+ .numeric-negative {{ color: red; font-weight: bold; }}
142
+ .top-up {{ background: #b6f2b6; }}
143
+ .top-down {{ background: #f2b6b6; }}
144
+ .grid {{ display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px; }}
145
+ .small-table {{ background: #fff; padding: 8px; border-radius: 6px; border: 1px solid #ddd; }}
146
+ .st-title {{ text-align: center; font-weight: bold; background: #222; color: #fff; padding: 6px; }}
147
+ .st-body {{ max-height: 300px; overflow-y: auto; }}
148
+ .mini-card-container {{ display: flex; flex-wrap: wrap; gap: 10px; }}
149
+ .mini-card {{ background: #fff; padding: 8px 10px; border-radius: 6px; border: 1px solid #ddd; min-width: 120px; }}
150
+ .card-key {{ font-weight: bold; }}
151
+ </style>
152
+ </head>
153
+ <body>
154
+
155
+ <h2>Pre-Open Market — {key}</h2>
156
+
157
+ <h3>Info</h3>
158
+ {info_cards_html}
159
+
160
+ <h3>Constituents</h3>
161
+ {cons_html}
162
+
163
+ <h3>Key Metrics</h3>
164
+ <div class="grid">
165
+ {metric_tables}
166
+ </div>
167
+
168
+ </body>
169
+ </html>
170
+ """
171
+
172
+ # ================= SAVE (HTML ONLY) =================
173
+ save(cache_name, html_out, "html")
174
+
175
+ return html_out