eshan6704 commited on
Commit
bb43ebf
·
verified ·
1 Parent(s): 3bbb084

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +316 -0
app.py ADDED
@@ -0,0 +1,316 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import yfinance as yf
3
+ import plotly.graph_objs as go
4
+ import pandas as pd
5
+
6
+ STYLE_BLOCK = """
7
+ <style>
8
+ .styled-table {
9
+ border-collapse: collapse;
10
+ margin: 10px 0;
11
+ font-size: 0.9em;
12
+ font-family: sans-serif;
13
+ width: 100%;
14
+ box-shadow: 0 0 10px rgba(0,0,0,0.1);
15
+ }
16
+ .styled-table th, .styled-table td {
17
+ padding: 8px 10px;
18
+ border: 1px solid #ddd;
19
+ }
20
+ .styled-table tbody tr:nth-child(even) {
21
+ background-color: #f9f9f9;
22
+ }
23
+ .card {
24
+ display: block; /* Ensures each card is on its own line */
25
+ width: 95%; /* Make card take up most of the width */
26
+ margin: 10px auto; /* Center the cards and add margin */
27
+ padding: 15px;
28
+ border: 1px solid #ddd;
29
+ border-radius: 8px;
30
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
31
+ background: #fafafa;
32
+ }
33
+ .card-category-title {
34
+ font-size: 1.1em; /* Slightly larger heading for category */
35
+ color: #222;
36
+ margin: 0 0 8px; /* Adjusted margin */
37
+ border-bottom: 1px solid #eee; /* Add a separator */
38
+ padding-bottom: 5px;
39
+ }
40
+ .card-content-grid {
41
+ display: flex;
42
+ flex-wrap: wrap; /* Allow items to wrap to the next line */
43
+ gap: 15px; /* Space between individual key-value items */
44
+ }
45
+ .key-value-pair {
46
+ flex: 1 1 calc(33% - 15px); /* For 3 items in a row, considering gap */
47
+ box-sizing: border-box; /* Include padding and border in the width */
48
+ min-width: 250px; /* Prevent items from becoming too narrow */
49
+ background: #fff;
50
+ padding: 10px;
51
+ border: 1px solid #e0e0e0;
52
+ border-radius: 5px;
53
+ box-shadow: 0 1px 3px rgba(0,0,0,0.05);
54
+ }
55
+ .key-value-pair h3 {
56
+ font-size: 0.95em; /* Smaller heading for the key */
57
+ color: #444;
58
+ margin: 0 0 5px 0;
59
+ border-bottom: none;
60
+ padding-bottom: 0;
61
+ }
62
+ .key-value-pair p {
63
+ font-size: 0.9em; /* Smaller paragraph for the value */
64
+ color: #555;
65
+ margin: 0;
66
+ font-weight: bold; /* Make values stand out */
67
+ }
68
+ .big-box {
69
+ width:95%;
70
+ margin:20px auto;
71
+ padding:20px;
72
+ border:1px solid #ccc;
73
+ border-radius:8px;
74
+ background:#fff;
75
+ box-shadow:0 2px 8px rgba(0,0,0,0.1);
76
+ font-size:0.95em;
77
+ line-height:1.4em;
78
+ max-height:400px;
79
+ overflow-y:auto;
80
+ }
81
+ </style>
82
+ """
83
+
84
+ def fetch_data(symbol, req_type):
85
+ try:
86
+ ticker = yf.Ticker(symbol)
87
+
88
+ content_html = ""
89
+
90
+ # Info block as cards + big boxes
91
+ if req_type.lower() == "info":
92
+ info = ticker.info
93
+ if not info:
94
+ content_html = "<h1>No info available</h1>"
95
+ else:
96
+ info_categories = {
97
+ "Company Overview": [
98
+ "longName", "symbol", "exchange", "quoteType", "sector", "industry",
99
+ "fullTimeEmployees", "website", "address1", "city", "state", "zip", "country", "phone"
100
+ ],
101
+ "Valuation Metrics": [
102
+ "marketCap", "enterpriseValue", "trailingPE", "forwardPE", "pegRatio",
103
+ "priceToSalesTrailing12Months", "enterpriseToRevenue", "enterpriseToEbitda"
104
+ ],
105
+ "Key Financials": [
106
+ "fiftyTwoWeekHigh", "fiftyTwoWeekLow", "fiftyDayAverage", "twoHundredDayAverage",
107
+ "trailingAnnualDividendRate", "trailingAnnualDividendYield", "dividendRate", "dividendYield",
108
+ "exDividendDate", "lastSplitFactor", "lastSplitDate", "lastDividendValue", "payoutRatio",
109
+ "beta", "sharesOutstanding", "impliedSharesOutstanding"
110
+ ],
111
+ "Operational Details": [
112
+ "auditRisk", "boardRisk", "compensationRisk", "shareHolderRightsRisk", "overallRisk",
113
+ "governanceEpochDate", "compensationAsOfEpochDate"
114
+ ],
115
+ "Trading Information": [
116
+ "open", "previousClose", "dayLow", "dayHigh", "volume", "averageVolume", "averageVolume10days",
117
+ "fiftyTwoWeekChange", "SandP52WeekChange", "currency", "regularMarketDayLow",
118
+ "regularMarketDayHigh", "regularMarketOpen", "regularMarketPreviousClose",
119
+ "regularMarketPrice", "regularMarketVolume", "regularMarketChange", "regularMarketChangePercent"
120
+ ],
121
+ "Analyst & Target": [
122
+ "targetMeanPrice", "numberOfAnalystOpinions", "recommendationKey", "recommendationMean"
123
+ ]
124
+ }
125
+
126
+ long_summary = info.pop("longBusinessSummary", None)
127
+ officers = info.pop("companyOfficers", None)
128
+
129
+ categorized_html = ""
130
+ for category_name, keys in info_categories.items():
131
+ category_key_value_html = "" # Collect key-value pairs for this category
132
+ for key in keys:
133
+ if key in info and info[key] is not None and info[key] != []:
134
+ value = info[key]
135
+ # Format values as appropriate
136
+ if isinstance(value, (int, float)) and key not in ['longName', 'symbol', 'exchange', 'quoteType', 'sector', 'industry', 'website', 'address1', 'city', 'state', 'zip', 'country', 'phone', 'longBusinessSummary', 'recommendationKey']:
137
+ if 'marketCap' in key.lower() or 'value' in key.lower() or 'volume' in key.lower():
138
+ value = f"{value:,.0f}" # Format large numbers
139
+ elif 'percent' in key.lower() or 'ratio' in key.lower() or 'yield' in key.lower() or 'beta' in key.lower() or 'payoutRatio' in key.lower():
140
+ value = f"{value:.2%}" # Format percentages
141
+ elif 'price' in key.lower() or 'dividend' in key.lower() or 'average' in key.lower():
142
+ value = f"{value:.2f}" # Format currency/prices
143
+
144
+ category_key_value_html += f"<div class='key-value-pair'><h3>{key.replace('_', ' ').title()}</h3><p>{value}</p></div>"
145
+
146
+ if category_key_value_html: # Only add category header and card if there is content in it
147
+ categorized_html += f"<h2>{category_name}</h2><div class='card'><div class='card-content-grid'>{category_key_value_html}</div></div>"
148
+
149
+ extra_sections = ""
150
+ if long_summary:
151
+ extra_sections += f"<div class='big-box'><h2>Business Summary</h2><p>{long_summary}</p></div>"
152
+ if officers:
153
+ officer_rows = "".join(
154
+ f"<tr><td>{o.get('name','')}"f"</td><td>{o.get('title','')}"f"</td><td>{o.get('age','')}"f"</td></tr>"
155
+ for o in officers
156
+ )
157
+ officer_table = f"<table class='styled-table'><tr><th>Name</th><th>Title</th><th>Age</th></tr>{officer_rows}</table>"
158
+ extra_sections += f"<div class='big-box'><h2>Company Officers</h2>{officer_table}</div>"
159
+ content_html = f"{categorized_html}{extra_sections}"
160
+
161
+ # Daily chart
162
+ elif req_type.lower() == "daily":
163
+ df = yf.download(symbol, period="1y", interval="1d").round(2)
164
+ if df.empty:
165
+ content_html = f"<h1>No daily data for {symbol}</h1>"
166
+ else:
167
+ if isinstance(df.columns, pd.MultiIndex):
168
+ df.columns = df.columns.get_level_values(0)
169
+
170
+ low_price = df["Low"].min()
171
+ high_price = df["High"].max()
172
+ price_range = high_price - low_price
173
+ vol_band_min = low_price - (price_range / 5)
174
+ vol_band_max = low_price
175
+ vol_max = df["Volume"].max()
176
+ vol_scale = (vol_band_max - vol_band_min) / vol_max if vol_max > 0 else 1
177
+
178
+ fig = go.Figure()
179
+ fig.add_trace(go.Candlestick(
180
+ x=df.index, open=df["Open"], high=df["High"],
181
+ low=df["Low"], close=df["Close"], name="Price"
182
+ ))
183
+ fig.add_trace(go.Bar(
184
+ x=df.index,
185
+ y=df["Volume"] * vol_scale + vol_band_min,
186
+ name="Volume", marker_color="lightblue",
187
+ customdata=df["Volume"],
188
+ hovertemplate="Volume: %{customdata}<extra></extra>"
189
+ ))
190
+ fig.update_layout(
191
+ xaxis_title="Date", yaxis_title="Price",
192
+ yaxis=dict(range=[vol_band_min, high_price]),
193
+ xaxis_rangeslider_visible=False, height=600
194
+ )
195
+ chart_html = fig.to_html(full_html=False)
196
+ table_html = df.tail(30).to_html(classes="styled-table", border=0)
197
+ content_html = f"{chart_html}<h2>Recent Daily Data (last 30 rows)</h2>{table_html}"
198
+
199
+ # Intraday chart
200
+ elif req_type.lower() == "intraday":
201
+ df = yf.download(symbol, period="1d", interval="5m").round(2)
202
+ if df.empty:
203
+ content_html = f"<h1>No intraday data for {symbol}</h1>"
204
+ else:
205
+ if isinstance(df.columns, pd.MultiIndex):
206
+ df.columns = df.columns.get_level_values(0)
207
+
208
+ low_price = df["Low"].min()
209
+ high_price = df["High"].max()
210
+ price_range = high_price - low_price
211
+ vol_band_min = low_price - (price_range / 5)
212
+ vol_band_max = low_price
213
+ vol_max = df["Volume"].max()
214
+ vol_scale = (vol_band_max - vol_band_min) / vol_max if vol_max > 0 else 1
215
+
216
+ fig = go.Figure()
217
+ fig.add_trace(go.Candlestick(
218
+ x=df.index, open=df["Open"], high=df["High"],
219
+ low=df["Low"], close=df["Close"], name="Price"
220
+ ))
221
+ fig.add_trace(go.Bar(
222
+ x=df.index,
223
+ y=df["Volume"] * vol_scale + vol_band_min,
224
+ name="Volume", marker_color="orange",
225
+ customdata=df["Volume"],
226
+ hovertemplate="Volume: %{customdata}<extra></extra>"
227
+ ))
228
+ fig.update_layout(
229
+ xaxis_title="Time", yaxis_title="Price",
230
+ yaxis=dict(range=[vol_band_min, high_price]),
231
+ xaxis_rangeslider_visible=False, height=600
232
+ )
233
+ chart_html = fig.to_html(full_html=False)
234
+ table_html = df.tail(50).to_html(classes="styled-table", border=0)
235
+ content_html = f"{chart_html}<h2>Recent Intraday Data (last 50 rows)</h2>{table_html}"
236
+
237
+ # Financial sections
238
+ elif req_type.lower() == "qresult":
239
+ df = ticker.quarterly_financials
240
+ content_html = f"<h2>Quarterly Results</h2>{df.to_html(classes='styled-table', border=0)}" if not df.empty else "<h1>No quarterly results available</h1>"
241
+
242
+ elif req_type.lower() == "result":
243
+ df = ticker.financials
244
+ content_html = f"<h2>Annual Results</h2>{df.to_html(classes='styled-table', border=0)}" if not df.empty else "<h1>No annual results available</h1>"
245
+
246
+ elif req_type.lower() == "balance":
247
+ df = ticker.balance_sheet
248
+ content_html = f"<h2>Balance Sheet</h2>{df.to_html(classes='styled-table', border=0)}" if not df.empty else "<h1>No balance sheet available</h1>"
249
+
250
+ elif req_type.lower() == "cashflow":
251
+ df = ticker.cashflow
252
+ content_html = f"<h2>Cash Flow</h2>{df.to_html(classes='styled-table', border=0)}" if not df.empty else "<h1>No cashflow available</h1>"
253
+
254
+ elif req_type.lower() == "dividend":
255
+ s = ticker.dividends
256
+ content_html = f"<h2>Dividend History</h2>{s.to_frame('Dividend').to_html(classes='styled-table', border=0)}" if not s.empty else "<h1>No dividend history available</h1>"
257
+
258
+ elif req_type.lower() == "split":
259
+ s = ticker.splits
260
+ content_html = f"<h2>Split History</h2>{s.to_frame('Split').to_html(classes='styled-table', border=0)}" if not s.empty else "<h1>No split history available</h1>"
261
+
262
+ elif req_type.lower() == "other":
263
+ df = ticker.earnings
264
+ content_html = f"<h2>Earnings</h2>{df.to_html(classes='styled-table', border=0)}" if not df.empty else "<h1>No earnings data available</h1>"
265
+
266
+ else:
267
+ content_html = f"<h1>No handler for {req_type}</h1>"
268
+
269
+ except Exception as e:
270
+ content_html = f"<h1>Error</h1><p>{str(e)}</p>"
271
+
272
+ # Wrap the content_html in a complete HTML document structure
273
+ full_html_output = f"""
274
+ <!DOCTYPE html>
275
+ <html>
276
+ <head>
277
+ <title>Stock Data for {symbol}</title>
278
+ {STYLE_BLOCK}
279
+ </head>
280
+ <body>
281
+ {content_html}
282
+ </body>
283
+ </html>
284
+ """
285
+ return full_html_output
286
+
287
+
288
+ iface = gr.Interface(
289
+ fn=fetch_data,
290
+ inputs=[
291
+ gr.Textbox(label="Stock Symbol", value="PNB.NS"),
292
+ gr.Dropdown(
293
+ label="Request Type",
294
+ choices=[
295
+ "info",
296
+ "intraday",
297
+ "daily",
298
+ "qresult",
299
+ "result",
300
+ "balance",
301
+ "cashflow",
302
+ "dividend",
303
+ "split",
304
+ "other"
305
+ ],
306
+ value="info"
307
+ )
308
+ ],
309
+ outputs=gr.HTML(label="Full HTML Output"),
310
+ title="Stock Data API (Full)",
311
+ description="Fetch data from NSE and yfinance",
312
+ api_name="fetch_data"
313
+ )
314
+
315
+ if __name__ == "__main__":
316
+ iface.launch(server_name="0.0.0.0", server_port=7860)