NeuSpaarX commited on
Commit
3ef75b2
Β·
verified Β·
1 Parent(s): 67f501e

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +544 -0
app.py ADDED
@@ -0,0 +1,544 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import plotly.graph_objects as go
4
+ import plotly.express as px
5
+ from datetime import datetime, timedelta
6
+ from phi.agent.agent import Agent
7
+ from phi.model.groq import Groq
8
+ from phi.tools.yfinance import YFinanceTools
9
+ from phi.tools.duckduckgo import DuckDuckGo
10
+ from phi.tools.googlesearch import GoogleSearch
11
+ import yfinance as yf
12
+
13
+ import os
14
+ from dotenv import load_dotenv
15
+
16
+ # Load environment variables from .env file
17
+ load_dotenv()
18
+
19
+ # Get API key from environment variables
20
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY")
21
+
22
+ # Add error handling
23
+ if not GROQ_API_KEY:
24
+ st.error("GROQ_API_KEY not found. Please check your .env file.")
25
+
26
+ # Enhanced stock symbol mappings
27
+ COMMON_STOCKS = {
28
+ # US Stocks
29
+ 'NVIDIA': 'NVDA',
30
+ 'APPLE': 'AAPL',
31
+ 'GOOGLE': 'GOOGL',
32
+ 'MICROSOFT': 'MSFT',
33
+ 'TESLA': 'TSLA',
34
+ 'AMAZON': 'AMZN',
35
+ 'META': 'META',
36
+ 'NETFLIX': 'NFLX',
37
+ # Indian Stocks - NSE
38
+ 'TCS': 'TCS.NS',
39
+ 'RELIANCE': 'RELIANCE.NS',
40
+ 'INFOSYS': 'INFY.NS',
41
+ 'WIPRO': 'WIPRO.NS',
42
+ 'HDFC': 'HDFCBANK.NS',
43
+ 'TATAMOTORS': 'TATAMOTORS.NS',
44
+ 'ICICIBANK': 'ICICIBANK.NS',
45
+ 'SBIN': 'SBIN.NS',
46
+ 'MARUTI': 'MARUTI.NS',
47
+ 'BHARTIARTL': 'BHARTIARTL.NS',
48
+ 'HCLTECH': 'HCLTECH.NS',
49
+ 'ITC': 'ITC.NS',
50
+ 'AXISBANK': 'AXISBANK.NS'
51
+ }
52
+
53
+ # Page configuration
54
+ st.set_page_config(
55
+ page_title="Advanced Stock Market Analysis",
56
+ page_icon="πŸ“ˆ",
57
+ layout="wide",
58
+ initial_sidebar_state="expanded"
59
+ )
60
+
61
+ # Custom CSS with improved styling
62
+ st.markdown("""
63
+ <style>
64
+ .main {
65
+ padding: 2rem;
66
+ }
67
+ .stApp {
68
+ max-width: 1400px;
69
+ margin: 0 auto;
70
+ }
71
+ .metric-card {
72
+ background-color: #f8f9fa;
73
+ border-radius: 10px;
74
+ padding: 1rem;
75
+ margin: 0.5rem 0;
76
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
77
+ transition: transform 0.2s;
78
+ }
79
+ .metric-card:hover {
80
+ transform: translateY(-2px);
81
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
82
+ }
83
+ .stock-header {
84
+ font-size: 28px;
85
+ font-weight: bold;
86
+ margin-bottom: 20px;
87
+ color: #f4e285;
88
+ text-align: center;
89
+ padding: 1rem;
90
+ background: #1a1f36;
91
+ border-radius: 10px;
92
+ }
93
+ .news-card {
94
+ background-color: white;
95
+ padding: 1rem;
96
+ border-radius: 5px;
97
+ margin: 10px 0;
98
+ border-left: 4px solid #1f77b4;
99
+ transition: transform 0.2s;
100
+ }
101
+ .news-card:hover {
102
+ transform: translateX(5px);
103
+ }
104
+ .stButton>button {
105
+ width: 100%;
106
+ }
107
+ .market-indicator {
108
+ font-size: 16px;
109
+ color: #666;
110
+ text-align: center;
111
+ margin-bottom: 1rem;
112
+ }
113
+ </style>
114
+ """, unsafe_allow_html=True)
115
+
116
+ # Initialize session state
117
+ if 'agents_initialized' not in st.session_state:
118
+ st.session_state.agents_initialized = False
119
+ st.session_state.watchlist = set()
120
+ st.session_state.analysis_history = []
121
+ st.session_state.last_refresh = None
122
+
123
+ def initialize_agents():
124
+ """Initialize all agent instances with improved error handling"""
125
+ if not st.session_state.agents_initialized:
126
+ try:
127
+ st.session_state.web_agent = Agent(
128
+ name="Web Search Agent",
129
+ role="Search the web for the information",
130
+ model=Groq(api_key=GROQ_API_KEY,
131
+ id="llama-3.3-70b-versatile"),
132
+ tools=[
133
+ GoogleSearch(fixed_language='english', fixed_max_results=5)
134
+ # DuckDuckGo(fixed_max_results=1)
135
+ ],
136
+ instructions=['Always include sources and verification'],
137
+ show_tool_calls=True,
138
+ markdown=True
139
+ )
140
+
141
+ st.session_state.finance_agent = Agent(
142
+ name="Financial AI Agent",
143
+ role="Providing financial insights",
144
+ model=Groq(api_key=GROQ_API_KEY,
145
+ id="llama-3.3-70b-versatile"),
146
+ tools=[
147
+ YFinanceTools(
148
+ stock_price=True,
149
+ company_news=True,
150
+ analyst_recommendations=True,
151
+ historical_prices=True
152
+ )
153
+ ],
154
+ instructions=["Provide detailed analysis with data visualization"],
155
+ show_tool_calls=True,
156
+ markdown=True
157
+ )
158
+
159
+ st.session_state.multi_ai_agent = Agent(
160
+ name='A Stock Market Agent',
161
+ role='A comprehensive assistant specializing in stock market analysis',
162
+ model=Groq(api_key=GROQ_API_KEY,
163
+ id="llama-3.3-70b-versatile"),
164
+ team=[st.session_state.web_agent, st.session_state.finance_agent],
165
+ instructions=["Provide comprehensive analysis with multiple data sources"],
166
+ show_tool_calls=True,
167
+ markdown=True
168
+ )
169
+
170
+ st.session_state.agents_initialized = True
171
+ return True
172
+ except Exception as e:
173
+ st.error(f"Error initializing agents: {str(e)}")
174
+ return False
175
+
176
+ def get_symbol_from_name(stock_name):
177
+ """Enhanced function to fetch stock symbol from full stock name"""
178
+ try:
179
+ # Clean up input
180
+ stock_name = stock_name.strip().upper()
181
+
182
+ # First check if it's in our common stocks dictionary
183
+ if stock_name in COMMON_STOCKS:
184
+ return COMMON_STOCKS[stock_name]
185
+
186
+ # Check if it's already a valid symbol
187
+ ticker = yf.Ticker(stock_name)
188
+ try:
189
+ info = ticker.info
190
+ if info and 'symbol' in info:
191
+ return stock_name
192
+ except:
193
+ pass
194
+
195
+ # Try Indian stock market (NSE)
196
+ try:
197
+ indian_symbol = f"{stock_name}.NS"
198
+ ticker = yf.Ticker(indian_symbol)
199
+ info = ticker.info
200
+ if info and 'symbol' in info:
201
+ return indian_symbol
202
+ except:
203
+ # Try BSE
204
+ try:
205
+ bse_symbol = f"{stock_name}.BO"
206
+ ticker = yf.Ticker(bse_symbol)
207
+ info = ticker.info
208
+ if info and 'symbol' in info:
209
+ return bse_symbol
210
+ except:
211
+ pass
212
+
213
+ st.error(f"Could not find valid symbol for {stock_name}")
214
+ return None
215
+ except Exception as e:
216
+ st.error(f"Error processing {stock_name}: {str(e)}")
217
+ return None
218
+
219
+ def get_stock_data(symbol, period="1y"):
220
+ """Enhanced function to fetch stock data with proper cache handling"""
221
+ try:
222
+ # Create a new ticker instance
223
+ stock = yf.Ticker(symbol)
224
+
225
+ # Fetch data with error handling
226
+ try:
227
+ info = stock.info
228
+ if not info:
229
+ raise ValueError("No data retrieved for symbol")
230
+ except Exception as info_error:
231
+ # If .NS suffix is missing for Indian stocks, try adding it
232
+ if not symbol.endswith('.NS') and not symbol.endswith('.BO'):
233
+ try:
234
+ indian_symbol = f"{symbol}.NS"
235
+ stock = yf.Ticker(indian_symbol)
236
+ info = stock.info
237
+ symbol = indian_symbol
238
+ except:
239
+ # Try Bombay Stock Exchange
240
+ try:
241
+ bse_symbol = f"{symbol}.BO"
242
+ stock = yf.Ticker(bse_symbol)
243
+ info = stock.info
244
+ symbol = bse_symbol
245
+ except:
246
+ raise info_error
247
+ else:
248
+ raise info_error
249
+
250
+ # Fetch historical data
251
+ hist = stock.history(period=period, interval="1d", auto_adjust=True)
252
+
253
+ if hist.empty:
254
+ raise ValueError("No historical data available")
255
+
256
+ return info, hist
257
+ except Exception as e:
258
+ st.error(f"Error fetching data for {symbol}: {str(e)}")
259
+ return None, None
260
+
261
+ def create_price_chart(hist_data, symbol):
262
+ """Create an interactive price chart using plotly"""
263
+ fig = go.Figure()
264
+
265
+ # Add candlestick chart
266
+ fig.add_trace(go.Candlestick(
267
+ x=hist_data.index,
268
+ open=hist_data['Open'],
269
+ high=hist_data['High'],
270
+ low=hist_data['Low'],
271
+ close=hist_data['Close'],
272
+ name='Price'
273
+ ))
274
+
275
+ # Add moving averages
276
+ ma20 = hist_data['Close'].rolling(window=20).mean()
277
+ ma50 = hist_data['Close'].rolling(window=50).mean()
278
+
279
+ fig.add_trace(go.Scatter(x=hist_data.index, y=ma20, name='20 Day MA', line=dict(color='orange')))
280
+ fig.add_trace(go.Scatter(x=hist_data.index, y=ma50, name='50 Day MA', line=dict(color='blue')))
281
+
282
+ fig.update_layout(
283
+ title=f'{symbol} Stock Price',
284
+ yaxis_title='Price',
285
+ template='plotly_white',
286
+ xaxis_rangeslider_visible=False,
287
+ height=600
288
+ )
289
+
290
+ return fig
291
+
292
+ def create_volume_chart(hist_data):
293
+ """Create enhanced volume chart using plotly"""
294
+ # Calculate volume moving average
295
+ volume_ma = hist_data['Volume'].rolling(window=20).mean()
296
+
297
+ fig = go.Figure()
298
+
299
+ # Add volume bars
300
+ fig.add_trace(go.Bar(
301
+ x=hist_data.index,
302
+ y=hist_data['Volume'],
303
+ name='Volume',
304
+ marker_color='rgba(31, 119, 180, 0.3)'
305
+ ))
306
+
307
+ # Add volume moving average
308
+ fig.add_trace(go.Scatter(
309
+ x=hist_data.index,
310
+ y=volume_ma,
311
+ name='20 Day Volume MA',
312
+ line=dict(color='red')
313
+ ))
314
+
315
+ fig.update_layout(
316
+ title='Trading Volume Analysis',
317
+ yaxis_title='Volume',
318
+ template='plotly_white',
319
+ height=400
320
+ )
321
+
322
+ return fig
323
+
324
+ def format_large_number(number):
325
+ """Format large numbers into readable format"""
326
+ if number >= 1e12:
327
+ return f"${number/1e12:.2f}T"
328
+ elif number >= 1e9:
329
+ return f"${number/1e9:.2f}B"
330
+ elif number >= 1e6:
331
+ return f"${number/1e6:.2f}M"
332
+ else:
333
+ return f"${number:,.2f}"
334
+
335
+ def display_metrics(info):
336
+ """Display enhanced key metrics in a grid"""
337
+ col1, col2, col3, col4 = st.columns(4)
338
+
339
+ with col1:
340
+ st.markdown('<div class="metric-card">', unsafe_allow_html=True)
341
+ market_cap = info.get('marketCap', 'N/A')
342
+ if market_cap != 'N/A':
343
+ market_cap = format_large_number(market_cap)
344
+ st.metric("Market Cap", market_cap)
345
+ st.markdown('</div>', unsafe_allow_html=True)
346
+
347
+ with col2:
348
+ st.markdown('<div class="metric-card">', unsafe_allow_html=True)
349
+ pe_ratio = info.get('trailingPE', 'N/A')
350
+ if pe_ratio != 'N/A':
351
+ pe_ratio = f"{pe_ratio:.2f}"
352
+ st.metric("P/E Ratio", pe_ratio)
353
+ st.markdown('</div>', unsafe_allow_html=True)
354
+
355
+ with col3:
356
+ st.markdown('<div class="metric-card">', unsafe_allow_html=True)
357
+ high = info.get('fiftyTwoWeekHigh', 'N/A')
358
+ if high != 'N/A':
359
+ high = f"${high:.2f}"
360
+ st.metric("52 Week High", high)
361
+ st.markdown('</div>', unsafe_allow_html=True)
362
+
363
+ with col4:
364
+ st.markdown('<div class="metric-card">', unsafe_allow_html=True)
365
+ low = info.get('fiftyTwoWeekLow', 'N/A')
366
+ if low != 'N/A':
367
+ low = f"${low:.2f}"
368
+ st.metric("52 Week Low", low)
369
+ st.markdown('</div>', unsafe_allow_html=True)
370
+
371
+ def main():
372
+ # Sidebar
373
+ with st.sidebar:
374
+ st.header("πŸ“Š Analysis Options")
375
+ analysis_type = st.selectbox(
376
+ "Choose Analysis Type",
377
+ ["Comprehensive Analysis", "Technical Analysis", "Fundamental Analysis",
378
+ "News Analysis", "Sentiment Analysis"]
379
+ )
380
+
381
+ # Add market selection
382
+ market = st.selectbox(
383
+ "Select Market",
384
+ ["US Market", "Indian Market (NSE)", "Indian Market (BSE)"]
385
+ )
386
+
387
+ st.markdown("---")
388
+
389
+
390
+
391
+ # Main content
392
+ st.markdown('<h1 class="stock-header">πŸ€– Advanced Stock Market Analysis By NeuSpaarX</h1>',
393
+ unsafe_allow_html=True)
394
+
395
+ # Search and Analysis Section
396
+ col1, col2 = st.columns([2, 1])
397
+ with col1:
398
+ stock_input = st.text_input(
399
+ "Enter Stock Name or Symbol",
400
+ help="Enter company name (e.g., NVIDIA) or symbol (e.g., NVDA)"
401
+ )
402
+ with col2:
403
+ date_range = st.selectbox(
404
+ "Select Time Range",
405
+ ["1 Month", "3 Months", "6 Months", "1 Year", "5 Years"], key="time_range"
406
+ )
407
+ # Convert selected range to yfinance period format
408
+ period_map = {
409
+ "1 Month": "1mo",
410
+ "3 Months": "3mo",
411
+ "6 Months": "6mo",
412
+ "1 Year": "1y",
413
+ "5 Years": "5y"
414
+ }
415
+ period = period_map[date_range]
416
+
417
+ if st.button("Analyze", type="primary"):
418
+ if not stock_input:
419
+ st.error("Please enter a stock name or symbol.")
420
+ return
421
+
422
+ # Convert input to symbol
423
+ stock_symbol = get_symbol_from_name(stock_input)
424
+ if stock_symbol:
425
+ try:
426
+ # Initialize agents
427
+ if initialize_agents():
428
+ # Show loading spinner
429
+ with st.spinner(f"Analyzing {stock_symbol}..."):
430
+ # Fetch fresh stock data
431
+ info, hist = get_stock_data(stock_symbol, period=period)
432
+
433
+ if info and hist is not None:
434
+ # Display market status
435
+ market_status = "🟒 Market Open" if info.get('regularMarketOpen') else "πŸ”΄ Market Closed"
436
+ st.markdown(f"<div class='market-indicator'>{market_status}</div>", unsafe_allow_html=True)
437
+
438
+ # Create tabs for different sections
439
+ overview_tab, charts_tab = st.tabs(["Overview", "Charts"])
440
+
441
+ with overview_tab:
442
+ # Display company info
443
+ st.markdown("### Company Overview")
444
+ st.write(info.get('longBusinessSummary', 'No description available.'))
445
+
446
+ # Display key metrics
447
+ st.markdown("### Key Metrics")
448
+ display_metrics(info)
449
+
450
+ # Additional company information
451
+ col1, col2 = st.columns(2)
452
+ with col1:
453
+ st.markdown("### Company Details")
454
+ st.write(f"Sector: {info.get('sector', 'N/A')}")
455
+ st.write(f"Industry: {info.get('industry', 'N/A')}")
456
+ st.write(f"Country: {info.get('country', 'N/A')}")
457
+ st.write(f"Employees: {info.get('fullTimeEmployees', 'N/A'):,}")
458
+
459
+ with col2:
460
+ st.markdown("### Trading Information")
461
+ st.write(f"Exchange: {info.get('exchange', 'N/A')}")
462
+ st.write(f"Currency: {info.get('currency', 'N/A')}")
463
+ st.write(f"Volume: {info.get('volume', 'N/A'):,}")
464
+
465
+ with charts_tab:
466
+ # Price chart
467
+ st.markdown("### Price Analysis")
468
+ price_chart = create_price_chart(hist, stock_symbol)
469
+ st.plotly_chart(price_chart, use_container_width=True)
470
+
471
+ # Volume chart
472
+ volume_chart = create_volume_chart(hist)
473
+ st.plotly_chart(volume_chart, use_container_width=True)
474
+
475
+ # Technical indicators
476
+ st.markdown("### Technical Indicators")
477
+ col1, col2, col3 = st.columns(3)
478
+
479
+ with col1:
480
+ rsi = hist['Close'].diff()
481
+ rsi_pos = rsi.copy()
482
+ rsi_neg = rsi.copy()
483
+ rsi_pos[rsi_pos < 0] = 0
484
+ rsi_neg[rsi_neg > 0] = 0
485
+ rsi_14_pos = rsi_pos.rolling(window=14).mean()
486
+ rsi_14_neg = abs(rsi_neg.rolling(window=14).mean())
487
+ rsi_14 = 100 - (100 / (1 + rsi_14_pos / rsi_14_neg))
488
+ st.metric("RSI (14)", f"{rsi_14.iloc[-1]:.2f}")
489
+
490
+ with col2:
491
+ ma20 = hist['Close'].rolling(window=20).mean()
492
+ ma50 = hist['Close'].rolling(window=50).mean()
493
+ cross_signal = "Bullish" if ma20.iloc[-1] > ma50.iloc[-1] else "Bearish"
494
+ st.metric("MA Cross Signal", cross_signal)
495
+
496
+ with col3:
497
+ volatility = hist['Close'].pct_change().std() * (252 ** 0.5) * 100
498
+ st.metric("Annualized Volatility", f"{volatility:.2f}%")
499
+
500
+
501
+ # Add refresh button
502
+ if st.button("πŸ”„ Refresh Data"):
503
+ st.session_state.last_refresh = datetime.now()
504
+ st.experimental_rerun()
505
+
506
+ except Exception as e:
507
+ st.error(f"An error occurred: {str(e)}")
508
+
509
+ # Display analysis history
510
+ if st.session_state.analysis_history:
511
+ st.markdown("---")
512
+ st.markdown("### Recent Analysis History")
513
+ history_df = pd.DataFrame(st.session_state.analysis_history)
514
+ history_df['timestamp'] = history_df['timestamp'].dt.strftime('%Y-%m-%d %H:%M:%S')
515
+ st.dataframe(history_df, use_container_width=True)
516
+
517
+ # Footer
518
+ st.markdown("---")
519
+ st.markdown("### About")
520
+ st.markdown("""
521
+ This advanced stock market analysis tool combines:
522
+ - Real-time market data analysis
523
+ - AI-powered insights and predictions
524
+ - Technical and fundamental analysis
525
+ - News and sentiment analysis
526
+ - Interactive charts and visualizations
527
+
528
+ Features:
529
+ - Support for both US and Indian markets (NSE/BSE)
530
+ - Company name and symbol resolution
531
+ - Watchlist management
532
+ - Multiple timeframe analysis
533
+ - Technical indicators
534
+
535
+ Use the sidebar to configure your analysis preferences and manage your watchlist.
536
+ """)
537
+
538
+ # Display last refresh time if available
539
+ if st.session_state.last_refresh:
540
+ st.markdown(f"<div class='market-indicator'>Last refreshed: {st.session_state.last_refresh.strftime('%Y-%m-%d %H:%M:%S')}</div>",
541
+ unsafe_allow_html=True)
542
+
543
+ if __name__ == "__main__":
544
+ main()