airabbitX commited on
Commit
bccac95
·
verified ·
1 Parent(s): 5a89df0

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +711 -0
app.py ADDED
@@ -0,0 +1,711 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import yfinance as yf
3
+ import pandas as pd
4
+ import numpy as np
5
+ import matplotlib.pyplot as plt
6
+ import plotly.graph_objects as go
7
+ from plotly.subplots import make_subplots
8
+ import datetime as dt
9
+ import json
10
+ from io import StringIO
11
+
12
+ # Helper functions for data processing
13
+ def format_large_number(num):
14
+ """Format large numbers to K, M, B, T"""
15
+ if num is None or pd.isna(num):
16
+ return "N/A"
17
+
18
+ if isinstance(num, str):
19
+ return num
20
+
21
+ if abs(num) >= 1_000_000_000_000:
22
+ return f"{num / 1_000_000_000_000:.2f}T"
23
+ elif abs(num) >= 1_000_000_000:
24
+ return f"{num / 1_000_000_000:.2f}B"
25
+ elif abs(num) >= 1_000_000:
26
+ return f"{num / 1_000_000:.2f}M"
27
+ elif abs(num) >= 1_000:
28
+ return f"{num / 1_000:.2f}K"
29
+ else:
30
+ return f"{num:.2f}"
31
+
32
+ def get_ticker_info(ticker_symbol):
33
+ """Get basic information about a ticker"""
34
+ try:
35
+ ticker = yf.Ticker(ticker_symbol)
36
+ info = ticker.info
37
+
38
+ # Create a more readable format
39
+ important_info = {
40
+ "Name": info.get("shortName", "N/A"),
41
+ "Sector": info.get("sector", "N/A"),
42
+ "Industry": info.get("industry", "N/A"),
43
+ "Country": info.get("country", "N/A"),
44
+ "Market Cap": format_large_number(info.get("marketCap", "N/A")),
45
+ "Current Price": info.get("currentPrice", info.get("regularMarketPrice", "N/A")),
46
+ "52 Week High": info.get("fiftyTwoWeekHigh", "N/A"),
47
+ "52 Week Low": info.get("fiftyTwoWeekLow", "N/A"),
48
+ "Website": info.get("website", "N/A"),
49
+ "Business Summary": info.get("longBusinessSummary", "N/A")
50
+ }
51
+
52
+ # Convert to formatted string
53
+ info_str = ""
54
+ for key, value in important_info.items():
55
+ info_str += f"**{key}**: {value}\n\n"
56
+
57
+ return info_str
58
+ except Exception as e:
59
+ return f"Error retrieving ticker info: {str(e)}"
60
+
61
+ def get_historical_data(ticker_symbol, period, interval):
62
+ """Get historical price data and create a plotly chart"""
63
+ try:
64
+ ticker = yf.Ticker(ticker_symbol)
65
+ history = ticker.history(period=period, interval=interval)
66
+
67
+ if history.empty:
68
+ return "No historical data available for this ticker", None
69
+
70
+ # Create Plotly figure
71
+ fig = go.Figure()
72
+ fig.add_trace(go.Candlestick(
73
+ x=history.index,
74
+ open=history['Open'],
75
+ high=history['High'],
76
+ low=history['Low'],
77
+ close=history['Close'],
78
+ name='Price'
79
+ ))
80
+
81
+ # Add volume as bar chart
82
+ fig.add_trace(go.Bar(
83
+ x=history.index,
84
+ y=history['Volume'],
85
+ name='Volume',
86
+ yaxis='y2',
87
+ marker_color='rgba(0, 100, 80, 0.4)'
88
+ ))
89
+
90
+ # Layout with secondary y-axis
91
+ fig.update_layout(
92
+ title=f'{ticker_symbol} Price History',
93
+ yaxis_title='Price',
94
+ yaxis2=dict(
95
+ title='Volume',
96
+ overlaying='y',
97
+ side='right',
98
+ showgrid=False
99
+ ),
100
+ xaxis_rangeslider_visible=False,
101
+ height=500
102
+ )
103
+
104
+ return f"Successfully retrieved historical data for {ticker_symbol}", fig
105
+ except Exception as e:
106
+ return f"Error retrieving historical data: {str(e)}", None
107
+
108
+ def get_financial_data(ticker_symbol, statement_type, period_type):
109
+ """Get financial statements data"""
110
+ try:
111
+ ticker = yf.Ticker(ticker_symbol)
112
+
113
+ if statement_type == "Income Statement":
114
+ if period_type == "Annual":
115
+ data = ticker.income_stmt
116
+ else: # Quarterly
117
+ data = ticker.quarterly_income_stmt
118
+ elif statement_type == "Balance Sheet":
119
+ if period_type == "Annual":
120
+ data = ticker.balance_sheet
121
+ else: # Quarterly
122
+ data = ticker.quarterly_balance_sheet
123
+ elif statement_type == "Cash Flow":
124
+ if period_type == "Annual":
125
+ data = ticker.cashflow
126
+ else: # Quarterly
127
+ data = ticker.quarterly_cashflow
128
+
129
+ if data is None or data.empty:
130
+ return f"No {statement_type} data available for {ticker_symbol}"
131
+
132
+ # Format the DataFrame for display
133
+ data = data.fillna("N/A")
134
+ # Format date columns to be more readable
135
+ data.columns = [col.strftime('%Y-%m-%d') if hasattr(col, 'strftime') else str(col) for col in data.columns]
136
+
137
+ # HTML representation will be more readable in the UI
138
+ return data.to_html(classes="table table-striped")
139
+ except Exception as e:
140
+ return f"Error retrieving financial data: {str(e)}"
141
+
142
+ def get_company_news(ticker_symbol):
143
+ """Get latest news for the company"""
144
+ try:
145
+ ticker = yf.Ticker(ticker_symbol)
146
+ news = ticker.news
147
+
148
+ if not news:
149
+ return "No recent news available for this ticker"
150
+
151
+ # Format news items
152
+ formatted_news = ""
153
+ for i, item in enumerate(news[:5]): # Show top 5 news items
154
+ # Extract from nested content structure if present
155
+ news_item = item.get('content', item)
156
+
157
+ # Get title
158
+ title = news_item.get('title', 'No title')
159
+
160
+ # Get publisher
161
+ publisher = "Unknown publisher"
162
+ if 'provider' in news_item and isinstance(news_item['provider'], dict):
163
+ publisher = news_item['provider'].get('displayName', 'Unknown publisher')
164
+
165
+ # Get link
166
+ link = "#"
167
+ if 'clickThroughUrl' in news_item and isinstance(news_item['clickThroughUrl'], dict):
168
+ link = news_item['clickThroughUrl'].get('url', '#')
169
+ elif 'canonicalUrl' in news_item and isinstance(news_item['canonicalUrl'], dict):
170
+ link = news_item['canonicalUrl'].get('url', '#')
171
+
172
+ # Get date
173
+ publish_date = 'Unknown date'
174
+ if 'pubDate' in news_item:
175
+ publish_date = news_item['pubDate']
176
+
177
+ formatted_news += f"### {i+1}. {title}\n\n"
178
+ formatted_news += f"**Source**: {publisher} | **Date**: {publish_date}\n\n"
179
+ formatted_news += f"**Link**: [Read full article]({link})\n\n"
180
+
181
+ # Add description if available
182
+ if 'description' in news_item:
183
+ description = news_item['description']
184
+ # Limit description length and strip HTML tags
185
+ if len(description) > 200:
186
+ description = description[:200] + "..."
187
+ formatted_news += f"{description}\n\n"
188
+
189
+ formatted_news += "---\n\n"
190
+
191
+ return formatted_news
192
+ except Exception as e:
193
+ return f"Error retrieving news: {str(e)}"
194
+
195
+ def get_analyst_recommendations(ticker_symbol):
196
+ """Get analyst recommendations"""
197
+ try:
198
+ ticker = yf.Ticker(ticker_symbol)
199
+ recommendations = ticker.recommendations
200
+
201
+ if recommendations is None or recommendations.empty:
202
+ return "No analyst recommendations available for this ticker"
203
+
204
+ # Create a figure for visualization
205
+ fig = plt.figure(figsize=(10, 6))
206
+
207
+ # Count occurrences of each recommendation
208
+ rec_counts = recommendations['To Grade'].value_counts()
209
+
210
+ # Create a pie chart
211
+ plt.pie(rec_counts, labels=rec_counts.index, autopct='%1.1f%%',
212
+ shadow=True, startangle=90, colors=['#ff9999','#66b3ff','#99ff99','#ffcc99','#c2c2f0'])
213
+
214
+ plt.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle
215
+ plt.title(f'Analyst Recommendations for {ticker_symbol}')
216
+
217
+ return f"Found {len(recommendations)} analyst recommendations for {ticker_symbol}", fig
218
+ except Exception as e:
219
+ return f"Error retrieving analyst recommendations: {str(e)}", None
220
+
221
+ def get_options_data(ticker_symbol, expiration_date=None):
222
+ """Get options chain data for the ticker"""
223
+ try:
224
+ ticker = yf.Ticker(ticker_symbol)
225
+
226
+ # Get available expiration dates
227
+ expirations = ticker.options
228
+
229
+ if not expirations:
230
+ return "No options data available for this ticker", None
231
+
232
+ # If no expiration date is provided or the provided one is invalid, use the first available
233
+ if expiration_date is None or expiration_date not in expirations:
234
+ expiration_date = expirations[0]
235
+
236
+ # Get options chain for the selected expiration date
237
+ options = ticker.option_chain(expiration_date)
238
+
239
+ calls = options.calls
240
+ puts = options.puts
241
+
242
+ # Prepare data for visualization
243
+ strike_prices = sorted(list(set(calls['strike'].tolist() + puts['strike'].tolist())))
244
+ call_volumes = []
245
+ put_volumes = []
246
+
247
+ for strike in strike_prices:
248
+ call_vol = calls[calls['strike'] == strike]['volume'].sum()
249
+ put_vol = puts[puts['strike'] == strike]['volume'].sum()
250
+ call_volumes.append(call_vol)
251
+ put_volumes.append(put_vol)
252
+
253
+ # Create figure for visualization
254
+ fig = plt.figure(figsize=(12, 6))
255
+
256
+ # Plot the data
257
+ plt.bar(np.array(strike_prices) - 0.2, call_volumes, width=0.4, label='Calls', color='green', alpha=0.6)
258
+ plt.bar(np.array(strike_prices) + 0.2, put_volumes, width=0.4, label='Puts', color='red', alpha=0.6)
259
+
260
+ plt.xlabel('Strike Price')
261
+ plt.ylabel('Volume')
262
+ plt.title(f'Options Volume for {ticker_symbol} (Expiry: {expiration_date})')
263
+ plt.legend()
264
+ plt.grid(True, alpha=0.3)
265
+
266
+ # Format for readability
267
+ current_price = ticker.info.get('regularMarketPrice', ticker.info.get('currentPrice', None))
268
+ if current_price:
269
+ plt.axvline(x=current_price, color='blue', linestyle='--', label=f'Current Price: {current_price}')
270
+ plt.legend()
271
+
272
+ # Create summary table data
273
+ summary = f"""
274
+ ### Options Summary for {ticker_symbol} (Expiry: {expiration_date})
275
+
276
+ **Available Expiration Dates:** {', '.join(expirations)}
277
+
278
+ #### Calls Summary:
279
+ - Count: {len(calls)}
280
+ - Total Volume: {calls['volume'].sum():,}
281
+ - Average Implied Volatility: {calls['impliedVolatility'].mean():.2%}
282
+
283
+ #### Puts Summary:
284
+ - Count: {len(puts)}
285
+ - Total Volume: {puts['volume'].sum():,}
286
+ - Average Implied Volatility: {puts['impliedVolatility'].mean():.2%}
287
+ """
288
+
289
+ return summary, fig
290
+ except Exception as e:
291
+ return f"Error retrieving options data: {str(e)}", None
292
+
293
+ def get_institutional_holders(ticker_symbol):
294
+ """Get institutional holders of the stock"""
295
+ try:
296
+ ticker = yf.Ticker(ticker_symbol)
297
+ holders = ticker.institutional_holders
298
+
299
+ if holders is None or holders.empty:
300
+ return "No institutional holders data available for this ticker", None
301
+
302
+ # Create figure for visualization
303
+ fig = plt.figure(figsize=(12, 6))
304
+
305
+ # Sort by percentage held
306
+ holders = holders.sort_values(by='% Out', ascending=False)
307
+
308
+ # Take top 10 holders for visualization
309
+ top_holders = holders.head(10)
310
+
311
+ # Plot the data
312
+ plt.barh(top_holders['Holder'], top_holders['% Out'] * 100)
313
+ plt.xlabel('Percentage Held (%)')
314
+ plt.ylabel('Institution')
315
+ plt.title(f'Top Institutional Holders of {ticker_symbol}')
316
+ plt.grid(True, alpha=0.3)
317
+
318
+ # Format x-axis as percentage
319
+ plt.gca().xaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{x:.1f}%'))
320
+
321
+ # Format the DataFrame for display
322
+ holders_html = holders.to_html(classes="table table-striped")
323
+
324
+ return holders_html, fig
325
+ except Exception as e:
326
+ return f"Error retrieving institutional holders: {str(e)}", None
327
+
328
+ def get_sector_industry_info(ticker_symbol):
329
+ """Get sector and industry information for the ticker"""
330
+ try:
331
+ ticker = yf.Ticker(ticker_symbol)
332
+ info = ticker.info
333
+
334
+ sector_key = info.get('sectorKey')
335
+ industry_key = info.get('industryKey')
336
+
337
+ if not sector_key or not industry_key:
338
+ return "Sector or industry information not available for this ticker"
339
+
340
+ try:
341
+ # Get sector information
342
+ sector = yf.Sector(sector_key)
343
+ sector_info = f"""
344
+ ### Sector Information
345
+
346
+ **Name:** {sector.name}
347
+ **Key:** {sector.key}
348
+ **Symbol:** {sector.symbol}
349
+
350
+ #### Overview
351
+ {sector.overview}
352
+
353
+ #### Top Companies in {sector.name} Sector
354
+ """
355
+ for company in sector.top_companies[:5]: # Show top 5 companies
356
+ sector_info += f"- {company.get('name', 'N/A')} ({company.get('symbol', 'N/A')})\n"
357
+
358
+ # Get industry information
359
+ industry = yf.Industry(industry_key)
360
+ industry_info = f"""
361
+ ### Industry Information
362
+
363
+ **Name:** {industry.name}
364
+ **Key:** {industry.key}
365
+ **Sector:** {industry.sector_name}
366
+
367
+ #### Top Performing Companies in {industry.name}
368
+ """
369
+ for company in industry.top_performing_companies[:5]: # Show top 5 companies
370
+ industry_info += f"- {company.get('name', 'N/A')} ({company.get('symbol', 'N/A')})\n"
371
+
372
+ return sector_info + industry_info
373
+ except Exception as e:
374
+ return f"Error retrieving sector/industry details: {str(e)}"
375
+ except Exception as e:
376
+ return f"Error retrieving sector/industry information: {str(e)}"
377
+
378
+ def search_stocks(query, max_results=10):
379
+ """Search for stocks using the YF Search API"""
380
+ try:
381
+ # First try with the standard approach
382
+ search_results = yf.Search(query, max_results=max_results)
383
+ quotes = search_results.quotes
384
+
385
+ if not quotes:
386
+ return "No search results found"
387
+
388
+ # Format the results
389
+ formatted_results = "### Search Results\n\n"
390
+
391
+ for quote in quotes:
392
+ symbol = quote.get('symbol', 'N/A')
393
+ name = quote.get('shortname', quote.get('longname', 'N/A'))
394
+ exchange = quote.get('exchange', 'N/A')
395
+ quote_type = quote.get('quoteType', 'N/A').capitalize()
396
+
397
+ formatted_results += f"**{symbol}** - {name}\n"
398
+ formatted_results += f"Exchange: {exchange} | Type: {quote_type}\n\n"
399
+
400
+ return formatted_results
401
+ except AttributeError as e:
402
+ if "has no attribute 'update'" in str(e):
403
+ # Alternative: Use the Ticker directly for basic information
404
+ try:
405
+ # If search fails, try to get info directly for the symbol
406
+ if len(query.strip()) <= 5: # Likely a symbol
407
+ ticker = yf.Ticker(query.strip())
408
+ info = ticker.info
409
+
410
+ formatted_results = "### Direct Ticker Results\n\n"
411
+ formatted_results += f"**{query.strip()}** - {info.get('shortName', 'N/A')}\n"
412
+ formatted_results += f"Exchange: {info.get('exchange', 'N/A')} | "
413
+ formatted_results += f"Type: {info.get('quoteType', 'N/A').capitalize()}\n\n"
414
+
415
+ return formatted_results
416
+ else:
417
+ return f"Search functionality unavailable due to version compatibility issue. If you know the exact ticker symbol, try entering it in the Single Ticker Analysis tab."
418
+ except:
419
+ return f"Search functionality unavailable due to version compatibility issue. If you know the exact ticker symbol, try entering it in the Single Ticker Analysis tab."
420
+ else:
421
+ return f"Error searching stocks: {str(e)}"
422
+ except Exception as e:
423
+ return f"Error searching stocks: {str(e)}"
424
+
425
+ def get_multi_ticker_comparison(ticker_symbols, period="1y"):
426
+ """Compare multiple tickers in a single chart"""
427
+ try:
428
+ if not ticker_symbols:
429
+ return "Please enter at least one ticker symbol", None
430
+
431
+ # Split input string into list of ticker symbols
432
+ tickers = [t.strip() for t in ticker_symbols.split() if t.strip()]
433
+
434
+ if not tickers:
435
+ return "Please enter at least one ticker symbol", None
436
+
437
+ # Download data for all tickers
438
+ data = yf.download(tickers, period=period, group_by='ticker')
439
+
440
+ if data.empty:
441
+ return "No data available for the provided tickers", None
442
+
443
+ # For a single ticker, the structure is different
444
+ if len(tickers) == 1:
445
+ ticker = tickers[0]
446
+ price_data = data['Close']
447
+ price_data.name = ticker
448
+ price_data = pd.DataFrame(price_data)
449
+ else:
450
+ # Extract closing prices for each ticker
451
+ price_data = pd.DataFrame()
452
+ for ticker in tickers:
453
+ try:
454
+ if (ticker, 'Close') in data.columns:
455
+ price_data[ticker] = data[ticker]['Close']
456
+ except:
457
+ continue
458
+
459
+ if price_data.empty:
460
+ return "Could not retrieve closing price data for the provided tickers", None
461
+
462
+ # Normalize the data to start at 100 for fair comparison
463
+ normalized_data = price_data.copy()
464
+ for col in normalized_data.columns:
465
+ normalized_data[col] = normalized_data[col] / normalized_data[col].iloc[0] * 100
466
+
467
+ # Create figure for visualization
468
+ fig = plt.figure(figsize=(12, 6))
469
+
470
+ for col in normalized_data.columns:
471
+ plt.plot(normalized_data.index, normalized_data[col], label=col)
472
+
473
+ plt.xlabel('Date')
474
+ plt.ylabel('Normalized Price (Base = 100)')
475
+ plt.title(f'Comparative Performance ({period})')
476
+ plt.legend()
477
+ plt.grid(True, alpha=0.3)
478
+
479
+ # Calculate performance metrics
480
+ performance = {}
481
+ for ticker in price_data.columns:
482
+ start_price = price_data[ticker].iloc[0]
483
+ end_price = price_data[ticker].iloc[-1]
484
+ pct_change = (end_price - start_price) / start_price * 100
485
+ performance[ticker] = pct_change
486
+
487
+ # Create a summary of the performance
488
+ summary = "### Performance Summary\n\n"
489
+ for ticker, pct in sorted(performance.items(), key=lambda x: x[1], reverse=True):
490
+ summary += f"**{ticker}**: {pct:.2f}%\n\n"
491
+
492
+ return summary, fig
493
+ except Exception as e:
494
+ return f"Error comparing tickers: {str(e)}", None
495
+
496
+ def get_market_status():
497
+ """Get current market status and summary"""
498
+ try:
499
+ # Get US market status
500
+ us_market = yf.Market("US")
501
+ status = us_market.status
502
+
503
+ if not status:
504
+ return "Unable to retrieve market status"
505
+
506
+ # Format the response
507
+ market_info = "### Market Status\n\n"
508
+
509
+ market_state = status.get('marketState', 'Unknown')
510
+ trading_status = "Open" if market_state == "REGULAR" else "Closed"
511
+
512
+ market_info += f"**US Market Status:** {trading_status} ({market_state})\n\n"
513
+
514
+ # Get summary for different markets
515
+ markets = ["US", "EUROPE", "ASIA", "CRYPTOCURRENCIES"]
516
+
517
+ for market_id in markets:
518
+ try:
519
+ market = yf.Market(market_id)
520
+ summary = market.summary
521
+
522
+ if summary is None:
523
+ market_info += f"### {market_id} Market Summary\n\nNo data available\n\n---\n\n"
524
+ continue
525
+
526
+ market_info += f"### {market_id} Market Summary\n\n"
527
+
528
+ # Make sure we handle the summary data correctly, regardless of its type
529
+ summary_items = []
530
+ if isinstance(summary, list):
531
+ summary_items = summary[:5] # Get first 5 items
532
+ elif hasattr(summary, '__getitem__'):
533
+ try:
534
+ summary_items = summary[:5] # Try to get first 5 items
535
+ except:
536
+ # If slicing fails, try to convert to list first
537
+ try:
538
+ summary_items = list(summary)[:5]
539
+ except:
540
+ summary_items = []
541
+
542
+ # Display market indices
543
+ if not summary_items:
544
+ market_info += "No summary data available\n\n"
545
+ else:
546
+ for item in summary_items:
547
+ if not isinstance(item, dict):
548
+ continue
549
+
550
+ symbol = item.get('symbol', 'N/A')
551
+ name = item.get('shortName', item.get('longName', 'N/A'))
552
+ price = item.get('regularMarketPrice', 'N/A')
553
+ change = item.get('regularMarketChangePercent', 0)
554
+
555
+ # Format change with color indicator
556
+ change_text = f"{change:.2f}%" if isinstance(change, (int, float)) else change
557
+ if isinstance(change, (int, float)):
558
+ if change > 0:
559
+ change_text = f"🟢 +{change_text}"
560
+ elif change < 0:
561
+ change_text = f"🔴 {change_text}"
562
+
563
+ market_info += f"**{name} ({symbol}):** {price} ({change_text})\n\n"
564
+
565
+ market_info += "---\n\n"
566
+ except Exception as e:
567
+ market_info += f"### {market_id} Market Summary\n\nError retrieving {market_id} market summary: {str(e)}\n\n---\n\n"
568
+
569
+ return market_info
570
+ except Exception as e:
571
+ return f"Error retrieving market status: {str(e)}"
572
+
573
+
574
+
575
+ # Gradio UI components
576
+ with gr.Blocks(title="YFinance Explorer") as app:
577
+ gr.Markdown("# YFinance Explorer\nA comprehensive tool to test all features of the yfinance library")
578
+
579
+ with gr.Tab("Single Ticker Analysis"):
580
+ with gr.Row():
581
+ ticker_input = gr.Textbox(label="Enter Ticker Symbol", placeholder="e.g. AAPL, MSFT, GOOG", value="AAPL")
582
+ ticker_submit = gr.Button("Analyze")
583
+
584
+ with gr.Tabs():
585
+ with gr.Tab("Overview"):
586
+ ticker_info_output = gr.Markdown()
587
+
588
+ with gr.Tab("Price History"):
589
+ with gr.Row():
590
+ period_dropdown = gr.Dropdown(
591
+ choices=["1d", "5d", "1mo", "3mo", "6mo", "1y", "2y", "5y", "10y", "ytd", "max"],
592
+ value="1y",
593
+ label="Period"
594
+ )
595
+ interval_dropdown = gr.Dropdown(
596
+ choices=["1m", "2m", "5m", "15m", "30m", "60m", "90m", "1h", "1d", "5d", "1wk", "1mo", "3mo"],
597
+ value="1d",
598
+ label="Interval"
599
+ )
600
+ history_status = gr.Markdown()
601
+ history_plot = gr.Plot()
602
+
603
+ with gr.Tab("Financials"):
604
+ with gr.Row():
605
+ statement_dropdown = gr.Dropdown(
606
+ choices=["Income Statement", "Balance Sheet", "Cash Flow"],
607
+ value="Income Statement",
608
+ label="Financial Statement"
609
+ )
610
+ period_type_dropdown = gr.Dropdown(
611
+ choices=["Annual", "Quarterly"],
612
+ value="Annual",
613
+ label="Period Type"
614
+ )
615
+ financial_data_output = gr.HTML()
616
+
617
+ with gr.Tab("News"):
618
+ news_output = gr.Markdown()
619
+
620
+
621
+
622
+ with gr.Tab("Multi-Ticker Comparison"):
623
+ with gr.Row():
624
+ multi_ticker_input = gr.Textbox(label="Enter Ticker Symbols (space separated)", placeholder="e.g. AAPL MSFT GOOG", value="AAPL MSFT GOOG")
625
+ comparison_period = gr.Dropdown(
626
+ choices=["1mo", "3mo", "6mo", "1y", "2y", "5y", "10y", "ytd", "max"],
627
+ value="1y",
628
+ label="Comparison Period"
629
+ )
630
+ compare_button = gr.Button("Compare")
631
+
632
+ comparison_status = gr.Markdown()
633
+ comparison_plot = gr.Plot()
634
+
635
+ with gr.Tab("Market Status"):
636
+ market_status_button = gr.Button("Get Market Status")
637
+ market_status_output = gr.Markdown()
638
+
639
+
640
+
641
+ with gr.Tab("Stock Search"):
642
+ with gr.Row():
643
+ search_input = gr.Textbox(label="Search Term", placeholder="Enter company name or ticker")
644
+ max_results_slider = gr.Slider(minimum=5, maximum=30, value=10, step=5, label="Max Results")
645
+ search_button = gr.Button("Search")
646
+
647
+ search_results = gr.Markdown()
648
+
649
+ # Event handlers
650
+ ticker_submit.click(
651
+ fn=get_ticker_info,
652
+ inputs=[ticker_input],
653
+ outputs=[ticker_info_output]
654
+ )
655
+
656
+ ticker_submit.click(
657
+ fn=get_historical_data,
658
+ inputs=[ticker_input, period_dropdown, interval_dropdown],
659
+ outputs=[history_status, history_plot]
660
+ )
661
+
662
+ ticker_submit.click(
663
+ fn=get_financial_data,
664
+ inputs=[ticker_input, statement_dropdown, period_type_dropdown],
665
+ outputs=[financial_data_output]
666
+ )
667
+
668
+ ticker_submit.click(
669
+ fn=get_company_news,
670
+ inputs=[ticker_input],
671
+ outputs=[news_output]
672
+ )
673
+
674
+
675
+
676
+
677
+ compare_button.click(
678
+ fn=get_multi_ticker_comparison,
679
+ inputs=[multi_ticker_input, comparison_period],
680
+ outputs=[comparison_status, comparison_plot]
681
+ )
682
+
683
+ market_status_button.click(
684
+ fn=get_market_status,
685
+ inputs=[],
686
+ outputs=[market_status_output]
687
+ )
688
+
689
+
690
+
691
+ search_button.click(
692
+ fn=search_stocks,
693
+ inputs=[search_input, max_results_slider],
694
+ outputs=[search_results]
695
+ )
696
+
697
+ # Update statement and interval options based on selections
698
+ def update_interval_choices(period):
699
+ if period in ["1d", "5d"]:
700
+ return gr.Dropdown.update(choices=["1m", "2m", "5m", "15m", "30m", "60m", "90m", "1h"], value="1m")
701
+ else:
702
+ return gr.Dropdown.update(choices=["1d", "5d", "1wk", "1mo", "3mo"], value="1d")
703
+
704
+ period_dropdown.change(
705
+ fn=update_interval_choices,
706
+ inputs=[period_dropdown],
707
+ outputs=[interval_dropdown]
708
+ )
709
+
710
+ if __name__ == "__main__":
711
+ app.launch()