bala00712200502 commited on
Commit
a34979d
Β·
verified Β·
1 Parent(s): 95ed569

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +275 -2030
app.py CHANGED
@@ -1,1998 +1,3 @@
1
- # # import streamlit as st
2
- # # import yfinance as yf
3
- # # import pandas as pd
4
- # # import plotly.graph_objects as go
5
- # # from datetime import datetime, timedelta
6
- # # from openai import OpenAI
7
- # # import matplotlib.pyplot as plt
8
- # # import seaborn as sns
9
- # # import numpy as np
10
- # # from ta.momentum import RSIIndicator
11
- # # from ta.trend import MACD, SMAIndicator
12
- # # from ta.volatility import BollingerBands
13
- # # import os
14
- # # # Initialize OpenAI client
15
- # # client = OpenAI(api_key="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") # Replace with your actual OpenAI API key
16
- # # # Verify your key is properly set
17
- # # import openai
18
- # # openai.api_key = os.getenv("OPENAI_API_KEY")
19
- # # print(f"Key exists: {bool(openai.api_key)}")
20
- # # # Page configuration
21
- # # st.set_page_config(
22
- # # page_title="AI Stock Market Analyst",
23
- # # page_icon="πŸ“ˆ",
24
- # # layout="wide",
25
- # # initial_sidebar_state="expanded"
26
- # # )
27
-
28
- # # # Custom CSS for better styling
29
- # # def local_css(file_name):
30
- # # with open(file_name) as f:
31
- # # st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)
32
-
33
- # # local_css("style.css")
34
-
35
- # # # === Core Function: Get Live Stock Data ===
36
- # # def get_live_data(ticker, period="1mo"):
37
- # # """Fetch live stock data using yfinance with proper error handling"""
38
- # # try:
39
- # # stock = yf.Ticker(ticker)
40
- # # hist = stock.history(period=period)
41
-
42
- # # if hist.empty:
43
- # # return None, f"⚠️ No data found for ticker {ticker}"
44
-
45
- # # # Calculate technical indicators
46
- # # # RSI
47
- # # rsi_indicator = RSIIndicator(hist["Close"], window=14)
48
- # # hist["RSI"] = rsi_indicator.rsi()
49
-
50
- # # # Moving Averages
51
- # # sma_20 = SMAIndicator(hist["Close"], window=20)
52
- # # sma_50 = SMAIndicator(hist["Close"], window=50)
53
- # # hist["MA_20"] = sma_20.sma_indicator()
54
- # # hist["MA_50"] = sma_50.sma_indicator()
55
-
56
- # # # MACD
57
- # # macd = MACD(hist["Close"])
58
- # # hist["MACD"] = macd.macd()
59
- # # hist["MACD_Signal"] = macd.macd_signal()
60
- # # hist["MACD_Hist"] = macd.macd_diff()
61
-
62
- # # # Bollinger Bands
63
- # # bb = BollingerBands(hist["Close"])
64
- # # hist["BB_Upper"] = bb.bollinger_hband()
65
- # # hist["BB_Lower"] = bb.bollinger_lband()
66
-
67
- # # return hist, None
68
-
69
- # # except Exception as e:
70
- # # return None, f"❌ Error fetching data for {ticker}: {str(e)}"
71
-
72
- # # # === Core Function: Stock Analysis with GPT-3.5 ===
73
- # # def analyze_stock_with_gpt(query, ticker=None, data=None, error_msg=None):
74
- # # """Get stock analysis from OpenAI GPT-3.5 with error handling"""
75
- # # try:
76
- # # # Prepare the prompt
77
- # # prompt = f"""
78
- # # You are a senior stock market analyst with 20 years of experience. Analyze the following query: '{query}'
79
-
80
- # # {f"Ticker mentioned: {ticker}" if ticker else "No specific ticker mentioned"}
81
- # # {error_msg if error_msg else ""}
82
-
83
- # # Expected output format (use markdown with emojis for better readability):
84
-
85
- # # ### πŸ“ˆ Analysis Report for {ticker if ticker else 'the stock'}
86
-
87
- # # **1. Fundamental Overview**
88
- # # - Current price: [if available]
89
- # # - Key metrics: [if available]
90
-
91
- # # **2. Technical Analysis**
92
- # # - Trend analysis
93
- # # - Key indicators (RSI, MACD, Moving Averages)
94
- # # - Support/resistance levels
95
-
96
- # # **3. Market Sentiment**
97
- # # - Recent performance
98
- # # - Volume analysis
99
-
100
- # # **4. Recommendation**
101
- # # - Clear buy/sell/hold recommendation with reasoning
102
- # # - Price targets if possible
103
- # # - Risk assessment
104
-
105
- # # **5. Next Steps**
106
- # # - What to watch for in coming days
107
-
108
- # # {data.tail(10)[['Close', 'RSI', 'MA_20', 'MA_50', 'Volume']].to_string() if data is not None else 'No data available'}
109
-
110
- # # IF DATA NOT AVALBLE YOU CAN GGIVE DAMMY DATA AS YOU OWN
111
-
112
- # # """
113
-
114
- # # response = client.chat.completions.create(
115
- # # model="gpt-3.5-turbo",
116
- # # messages=[
117
- # # {"role": "system", "content": "You are a professional stock market analyst with expertise in technical and fundamental analysis. Provide detailed but concise analysis with clear recommendations.IF DATA WAS NOT AVLABLE SHOW ANY GRAPH ANY DATA GIVE DAMMY DATA TABLE FORMET "},
118
- # # {"role": "user", "content": prompt}
119
- # # ],
120
- # # temperature=0.9
121
- # # )
122
-
123
- # # return response.choices[0].message.content
124
-
125
- # # except Exception as e:
126
- # # return f"❌ Error in analysis: {str(e)}"
127
-
128
- # # # === Visualization Functions ===
129
- # # def create_price_chart(data, ticker):
130
- # # fig = go.Figure()
131
-
132
- # # # Price line
133
- # # fig.add_trace(go.Scatter(
134
- # # x=data.index, y=data['Close'],
135
- # # name='Price',
136
- # # line=dict(color='#1f77b4', width=2)
137
- # # ))
138
-
139
- # # # Moving averages
140
- # # fig.add_trace(go.Scatter(
141
- # # x=data.index, y=data['MA_20'],
142
- # # name='20-day MA',
143
- # # line=dict(color='orange', width=1)
144
- # # ))
145
-
146
- # # fig.add_trace(go.Scatter(
147
- # # x=data.index, y=data['MA_50'],
148
- # # name='50-day MA',
149
- # # line=dict(color='red', width=1)
150
- # # ))
151
-
152
- # # # Bollinger Bands
153
- # # fig.add_trace(go.Scatter(
154
- # # x=data.index, y=data['BB_Upper'],
155
- # # name='Upper BB',
156
- # # line=dict(color='gray', width=1, dash='dot')
157
- # # ))
158
-
159
- # # fig.add_trace(go.Scatter(
160
- # # x=data.index, y=data['BB_Lower'],
161
- # # name='Lower BB',
162
- # # line=dict(color='gray', width=1, dash='dot'),
163
- # # fill='tonexty',
164
- # # fillcolor='rgba(200,200,200,0.2)'
165
- # # ))
166
-
167
- # # fig.update_layout(
168
- # # title=f'{ticker} Price Analysis',
169
- # # xaxis_title='Date',
170
- # # yaxis_title='Price ($)',
171
- # # hovermode='x unified',
172
- # # template='plotly_white',
173
- # # height=500
174
- # # )
175
-
176
- # # return fig
177
-
178
- # # def create_rsi_chart(data, ticker):
179
- # # fig = go.Figure()
180
-
181
- # # # RSI line
182
- # # fig.add_trace(go.Scatter(
183
- # # x=data.index, y=data['RSI'],
184
- # # name='RSI',
185
- # # line=dict(color='purple', width=2)
186
- # # ))
187
-
188
- # # # Overbought/oversold lines
189
- # # fig.add_hline(y=70, line_dash="dash", line_color="red", annotation_text="Overbought")
190
- # # fig.add_hline(y=30, line_dash="dash", line_color="green", annotation_text="Oversold")
191
-
192
- # # fig.update_layout(
193
- # # title=f'{ticker} RSI (14-day)',
194
- # # xaxis_title='Date',
195
- # # yaxis_title='RSI',
196
- # # hovermode='x unified',
197
- # # template='plotly_white',
198
- # # height=300
199
- # # )
200
-
201
- # # return fig
202
-
203
- # # def create_macd_chart(data, ticker):
204
- # # fig = go.Figure()
205
-
206
- # # # MACD line
207
- # # fig.add_trace(go.Scatter(
208
- # # x=data.index, y=data['MACD'],
209
- # # name='MACD',
210
- # # line=dict(color='blue', width=2)
211
- # # ))
212
-
213
- # # # Signal line
214
- # # fig.add_trace(go.Scatter(
215
- # # x=data.index, y=data['MACD_Signal'],
216
- # # name='Signal',
217
- # # line=dict(color='orange', width=2)
218
- # # ))
219
-
220
- # # # Histogram
221
- # # colors = np.where(data['MACD_Hist'] < 0, 'red', 'green')
222
- # # fig.add_trace(go.Bar(
223
- # # x=data.index, y=data['MACD_Hist'],
224
- # # name='Histogram',
225
- # # marker_color=colors,
226
- # # opacity=0.6
227
- # # ))
228
-
229
- # # fig.update_layout(
230
- # # title=f'{ticker} MACD',
231
- # # xaxis_title='Date',
232
- # # yaxis_title='Value',
233
- # # hovermode='x unified',
234
- # # template='plotly_white',
235
- # # height=300
236
- # # )
237
-
238
- # # return fig
239
-
240
- # # # === Streamlit UI ===
241
- # # def main():
242
- # # st.title("πŸ“ˆ AI Stock Market Analyst")
243
- # # st.markdown("""
244
- # # <style>
245
- # # .big-font {
246
- # # font-size:16px !important;
247
- # # color: #4a4a4a;
248
- # # }
249
- # # </style>
250
- # # """, unsafe_allow_html=True)
251
-
252
- # # st.markdown('<p class="big-font">Get AI-powered stock analysis with live data, technical indicators, and investment recommendations</p>', unsafe_allow_html=True)
253
-
254
- # # # Sidebar
255
- # # with st.sidebar:
256
- # # st.header("Settings")
257
- # # default_ticker = st.text_input("Default Ticker", value="AAPL")
258
- # # period_options = ["1d", "5d", "1mo", "3mo", "6mo", "1y", "2y", "5y"]
259
- # # selected_period = st.selectbox("Analysis Period", period_options, index=2)
260
-
261
- # # st.markdown("---")
262
- # # st.markdown("### Technical Indicators")
263
- # # show_rsi = st.checkbox("Show RSI", value=True)
264
- # # show_macd = st.checkbox("Show MACD", value=True)
265
- # # show_bb = st.checkbox("Show Bollinger Bands", value=True)
266
-
267
- # # st.markdown("---")
268
- # # st.markdown("""
269
- # # **How to use:**
270
- # # 1. Enter a stock ticker or ask a question
271
- # # 2. View live data and analysis
272
- # # 3. Get AI-powered recommendations
273
- # # """)
274
-
275
- # # # Main content
276
- # # col1, col2 = st.columns([3, 1])
277
-
278
- # # with col1:
279
- # # query = st.text_input("Ask about a stock (e.g., 'Analyze AAPL' or 'Should I buy Tesla?')",
280
- # # value=f"Analyze {default_ticker}")
281
-
282
- # # with col2:
283
- # # st.markdown("")
284
- # # st.markdown("")
285
- # # analyze_btn = st.button("Analyze", type="primary")
286
-
287
- # # if analyze_btn or query:
288
- # # # Attempt to extract ticker from query
289
- # # words = query.upper().split()
290
- # # possible_tickers = [word for word in words if word.isalpha() and len(word) <= 5]
291
- # # ticker = possible_tickers[-1] if possible_tickers else default_ticker
292
-
293
- # # st.markdown(f"### πŸ“Š {ticker} Analysis")
294
-
295
- # # with st.spinner(f"Fetching data and analyzing {ticker}..."):
296
- # # data, error_msg = get_live_data(ticker, selected_period)
297
-
298
- # # if data is not None and not data.empty:
299
- # # # Create tabs for different sections
300
- # # tab1, tab2, tab3 = st.tabs(["πŸ“ˆ Price Analysis", "πŸ“‹ Data", "🧠 AI Analysis"])
301
-
302
- # # with tab1:
303
- # # # Price chart
304
- # # st.plotly_chart(create_price_chart(data, ticker), use_container_width=True)
305
-
306
- # # # Indicators in columns
307
- # # col1, col2 = st.columns(2)
308
-
309
- # # with col1:
310
- # # if show_rsi:
311
- # # st.plotly_chart(create_rsi_chart(data, ticker), use_container_width=True)
312
-
313
- # # with col2:
314
- # # if show_macd:
315
- # # st.plotly_chart(create_macd_chart(data, ticker), use_container_width=True)
316
-
317
- # # # Key metrics
318
- # # st.subheader("Key Metrics")
319
- # # last_close = data['Close'].iloc[-1]
320
- # # last_rsi = data['RSI'].iloc[-1]
321
- # # ma_20 = data['MA_20'].iloc[-1]
322
- # # ma_50 = data['MA_50'].iloc[-1]
323
-
324
- # # metric_col1, metric_col2, metric_col3, metric_col4 = st.columns(4)
325
- # # metric_col1.metric("Current Price", f"${last_close:.2f}")
326
- # # metric_col2.metric("RSI (14-day)", f"{last_rsi:.1f}",
327
- # # "Overbought" if last_rsi > 70 else "Oversold" if last_rsi < 30 else "Neutral")
328
- # # metric_col3.metric("20-day MA", f"${ma_20:.2f}",
329
- # # f"{(last_close - ma_20):.2f} ({((last_close - ma_20)/ma_20*100):.2f}%)")
330
- # # metric_col4.metric("50-day MA", f"${ma_50:.2f}",
331
- # # f"{(last_close - ma_50):.2f} ({((last_close - ma_50)/ma_50*100):.2f}%)")
332
-
333
- # # with tab2:
334
- # # # Show recent data
335
- # # st.subheader("Recent Market Data")
336
- # # recent_data = data.tail(20)[['Open', 'High', 'Low', 'Close', 'Volume', 'RSI', 'MA_20', 'MA_50']]
337
- # # recent_data.index = recent_data.index.strftime('%Y-%m-%d')
338
- # # st.dataframe(recent_data.style.format({
339
- # # 'Open': '{:.2f}',
340
- # # 'High': '{:.2f}',
341
- # # 'Low': '{:.2f}',
342
- # # 'Close': '{:.2f}',
343
- # # 'RSI': '{:.1f}',
344
- # # 'MA_20': '{:.2f}',
345
- # # 'MA_50': '{:.2f}'
346
- # # }).background_gradient(cmap='Blues', subset=['RSI']),
347
- # # use_container_width=True)
348
-
349
- # # # Volume chart
350
- # # st.subheader("Trading Volume")
351
- # # fig_vol = go.Figure(go.Bar(
352
- # # x=data.index, y=data['Volume'],
353
- # # name='Volume',
354
- # # marker_color='#1f77b4'
355
- # # ))
356
- # # fig_vol.update_layout(
357
- # # height=300,
358
- # # template='plotly_white'
359
- # # )
360
- # # st.plotly_chart(fig_vol, use_container_width=True)
361
-
362
- # # with tab3:
363
- # # # Get AI analysis
364
- # # analysis = analyze_stock_with_gpt(query, ticker, data, error_msg)
365
- # # st.markdown(analysis)
366
- # # else:
367
- # # st.error(error_msg if error_msg else "No data available for this ticker")
368
- # # # Still try to get AI analysis
369
- # # st.markdown("### 🧠 AI Analysis")
370
- # # analysis = analyze_stock_with_gpt(query, ticker, None, error_msg)
371
- # # st.markdown(analysis)
372
-
373
- # # if __name__ == "__main__":
374
- # # main()
375
-
376
-
377
- # # import streamlit as st
378
- # # import yfinance as yf
379
- # # import pandas as pd
380
- # # import plotly.graph_objects as go
381
- # # from datetime import datetime, timedelta
382
- # # from openai import OpenAI
383
- # # import numpy as np
384
- # # from ta.momentum import RSIIndicator
385
- # # from ta.trend import MACD, SMAIndicator
386
- # # from ta.volatility import BollingerBands
387
- # # import os
388
- # # import random
389
- # # import time
390
- # # from typing import Tuple, Optional
391
-
392
- # # # Configuration
393
- # # MAX_RETRIES = 3
394
- # # RETRY_DELAY = 2
395
-
396
- # # # Initialize OpenAI client with robust error handling
397
- # # def init_openai_client():
398
- # # """Initialize OpenAI client with retry logic"""
399
- # # for attempt in range(MAX_RETRIES):
400
- # # try:
401
- # # api_key = os.getenv("OPENAI_API_KEY", "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")
402
- # # if not api_key or api_key == "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":
403
- # # raise ValueError("OpenAI API key not configured")
404
-
405
- # # client = OpenAI(api_key=api_key)
406
- # # # Test the connection
407
- # # client.models.list()
408
- # # return client
409
- # # except Exception as e:
410
- # # if attempt == MAX_RETRIES - 1:
411
- # # st.error(f"Failed to initialize OpenAI after {MAX_RETRIES} attempts: {str(e)}")
412
- # # return None
413
- # # time.sleep(RETRY_DELAY)
414
-
415
- # # client = init_openai_client()
416
-
417
- # # # Page configuration
418
- # # st.set_page_config(
419
- # # page_title="πŸ“Š AI Stock Analyst Pro+",
420
- # # page_icon="πŸ“Š",
421
- # # layout="wide",
422
- # # initial_sidebar_state="expanded"
423
- # # )
424
-
425
- # # # Custom CSS for modern styling
426
- # # st.markdown("""
427
- # # <style>
428
- # # .stApp {
429
- # # background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
430
- # # }
431
- # # .header {
432
- # # color: #2c3e50;
433
- # # text-shadow: 1px 1px 3px rgba(0,0,0,0.1);
434
- # # }
435
- # # .metric-box {
436
- # # background: white;
437
- # # border-radius: 10px;
438
- # # padding: 15px;
439
- # # box-shadow: 0 4px 6px rgba(0,0,0,0.1);
440
- # # transition: all 0.3s ease;
441
- # # }
442
- # # .metric-box:hover {
443
- # # transform: translateY(-3px);
444
- # # box-shadow: 0 6px 12px rgba(0,0,0,0.15);
445
- # # }
446
- # # .stTabs [aria-selected="true"] {
447
- # # background: linear-gradient(90deg, #3498db 0%, #2980b9 100%) !important;
448
- # # color: white !important;
449
- # # }
450
- # # .stButton>button {
451
- # # background: linear-gradient(90deg, #3498db 0%, #2980b9 100%);
452
- # # color: white;
453
- # # border: none;
454
- # # border-radius: 8px;
455
- # # padding: 10px 20px;
456
- # # font-weight: 600;
457
- # # }
458
- # # .error-box {
459
- # # background: #ffebee;
460
- # # border-left: 4px solid #f44336;
461
- # # padding: 1rem;
462
- # # margin: 1rem 0;
463
- # # }
464
- # # .warning-box {
465
- # # background: #fff8e1;
466
- # # border-left: 4px solid #ffc107;
467
- # # padding: 1rem;
468
- # # margin: 1rem 0;
469
- # # }
470
- # # </style>
471
- # # """, unsafe_allow_html=True)
472
-
473
- # # # === Core Data Functions ===
474
- # # def generate_dummy_data(ticker: str, days: int = 30) -> Tuple[pd.DataFrame, str]:
475
- # # """Generate realistic dummy stock data with proper technical indicators"""
476
- # # try:
477
- # # dates = pd.date_range(end=datetime.today(), periods=days)
478
- # # base_price = random.uniform(50, 200)
479
- # # volatility = random.uniform(1.0, 3.0) # More realistic volatility
480
-
481
- # # # Generate price series with momentum
482
- # # prices = []
483
- # # momentum = 0
484
- # # for i in range(days):
485
- # # # Add some momentum effect
486
- # # if i > 5 and random.random() > 0.7:
487
- # # momentum = random.uniform(-2, 2)
488
-
489
- # # change = random.uniform(-volatility, volatility) + momentum
490
- # # new_price = base_price * (1 + change/100)
491
-
492
- # # # Ensure prices don't go negative
493
- # # prices.append(max(1.0, new_price))
494
- # # base_price = prices[-1]
495
-
496
- # # # Create DataFrame with realistic OHLC data
497
- # # data = pd.DataFrame({
498
- # # 'Open': prices,
499
- # # 'High': [p * (1 + random.uniform(0, 0.02)) for p in prices],
500
- # # 'Low': [p * (1 - random.uniform(0, 0.02)) for p in prices],
501
- # # 'Close': prices,
502
- # # 'Volume': [int(random.gauss(2000000, 500000)) for _ in prices]
503
- # # }, index=dates)
504
-
505
- # # # Calculate technical indicators
506
- # # data = calculate_technical_indicators(data)
507
-
508
- # # return data, f"⚠️ Using simulated data for {ticker}"
509
- # # except Exception as e:
510
- # # st.error(f"Dummy data generation failed: {str(e)}")
511
- # # # Fallback to very simple data
512
- # # return pd.DataFrame({'Close': [100]}, index=[datetime.today()]), "⚠️ Basic fallback data"
513
-
514
- # # def calculate_technical_indicators(data: pd.DataFrame) -> pd.DataFrame:
515
- # # """Calculate all technical indicators for a dataframe"""
516
- # # try:
517
- # # # RSI
518
- # # if len(data) >= 14:
519
- # # data["RSI"] = RSIIndicator(data["Close"], window=14).rsi()
520
-
521
- # # # Moving Averages
522
- # # if len(data) >= 20:
523
- # # data["MA_20"] = SMAIndicator(data["Close"], window=20).sma_indicator()
524
- # # if len(data) >= 50:
525
- # # data["MA_50"] = SMAIndicator(data["Close"], window=50).sma_indicator()
526
-
527
- # # # MACD
528
- # # if len(data) >= 26: # MACD minimum period
529
- # # macd = MACD(data["Close"])
530
- # # data["MACD"] = macd.macd()
531
- # # data["MACD_Signal"] = macd.macd_signal()
532
- # # data["MACD_Hist"] = macd.macd_diff()
533
-
534
- # # # Bollinger Bands
535
- # # if len(data) >= 20: # BB minimum period
536
- # # bb = BollingerBands(data["Close"])
537
- # # data["BB_Upper"] = bb.bollinger_hband()
538
- # # data["BB_Lower"] = bb.bollinger_lband()
539
-
540
- # # return data
541
- # # except Exception as e:
542
- # # st.error(f"Technical indicator calculation failed: {str(e)}")
543
- # # return data
544
-
545
- # # def get_live_data(ticker: str, period: str = "1mo") -> Tuple[pd.DataFrame, Optional[str]]:
546
- # # """Fetch live stock data with retry logic and fallback"""
547
- # # for attempt in range(MAX_RETRIES):
548
- # # try:
549
- # # stock = yf.Ticker(ticker)
550
- # # hist = stock.history(period=period)
551
-
552
- # # if hist.empty:
553
- # # raise ValueError(f"No data found for {ticker}")
554
-
555
- # # hist = calculate_technical_indicators(hist)
556
- # # return hist, None
557
-
558
- # # except Exception as e:
559
- # # if attempt == MAX_RETRIES - 1:
560
- # # st.warning(f"Live data fetch failed, using simulated data: {str(e)}")
561
- # # return generate_dummy_data(ticker)
562
- # # time.sleep(RETRY_DELAY)
563
-
564
- # # # === AI Analysis Functions ===
565
- # # def analyze_stock_with_gpt(query: str, ticker: Optional[str] = None,
566
- # # data: Optional[pd.DataFrame] = None,
567
- # # error_msg: Optional[str] = None) -> str:
568
- # # """Robust stock analysis with multiple fallback options"""
569
- # # # Fallback analysis template
570
- # # fallback_analysis = f"""
571
- # # ## πŸ“Š Manual Analysis Report for {ticker if ticker else 'the stock'}
572
-
573
- # # **Market Overview**
574
- # # - Current market conditions are showing typical volatility
575
- # # - Sector performance appears mixed
576
-
577
- # # **Technical Assessment**
578
- # # - Key indicators suggest {random.choice(['neutral', 'slightly bullish', 'slightly bearish'])} momentum
579
- # # - Support/resistance levels not available
580
-
581
- # # **Recommendation**
582
- # # - **Action**: {random.choice(['Hold', 'Consider buying on dips', 'Consider partial profit taking'])}
583
- # # - **Risk Level**: Moderate
584
-
585
- # # **Note**: This is a fallback analysis. {error_msg or 'AI service unavailable'}
586
- # # """
587
-
588
- # # if client is None:
589
- # # return fallback_analysis
590
-
591
- # # try:
592
- # # # Prepare context for the AI
593
- # # context = {
594
- # # "query": query,
595
- # # "ticker": ticker,
596
- # # "error_msg": error_msg,
597
- # # "data_stats": data.describe().to_dict() if data is not None else None,
598
- # # "last_5_days": data.tail().to_dict() if data is not None else None
599
- # # }
600
-
601
- # # prompt = f"""
602
- # # As a senior stock analyst, provide comprehensive analysis with:
603
- # # 1. Market context
604
- # # 2. Technical assessment
605
- # # 3. Actionable recommendation
606
- # # 4. Risk analysis
607
-
608
- # # Context: {str(context)}
609
- # # """
610
-
611
- # # for attempt in range(MAX_RETRIES):
612
- # # try:
613
- # # response = client.chat.completions.create(
614
- # # model="gpt-3.5-turbo",
615
- # # messages=[
616
- # # {"role": "system", "content": "You are a professional stock analyst. Provide clear, data-driven insights."},
617
- # # {"role": "user", "content": prompt}
618
- # # ],
619
- # # temperature=0.6,
620
- # # timeout=15
621
- # # )
622
- # # return response.choices[0].message.content
623
- # # except Exception as e:
624
- # # if attempt == MAX_RETRIES - 1:
625
- # # st.warning(f"AI analysis failed after {MAX_RETRIES} attempts: {str(e)}")
626
- # # return fallback_analysis
627
- # # time.sleep(RETRY_DELAY)
628
-
629
- # # except Exception as e:
630
- # # st.error(f"Analysis system error: {str(e)}")
631
- # # return fallback_analysis
632
-
633
- # # # === Visualization Functions ===
634
- # # def create_price_chart(data: pd.DataFrame, ticker: str, is_dummy: bool = False) -> go.Figure:
635
- # # """Create interactive price chart with indicators"""
636
- # # fig = go.Figure()
637
-
638
- # # # Price line with conditional styling
639
- # # line_color = '#FF6B6B' if is_dummy else '#3498db'
640
- # # fig.add_trace(go.Scatter(
641
- # # x=data.index, y=data['Close'],
642
- # # name='Price',
643
- # # line=dict(color=line_color, width=2),
644
- # # hovertemplate='Date: %{x}<br>Price: $%{y:.2f}<extra></extra>'
645
- # # ))
646
-
647
- # # # Add available indicators
648
- # # if 'MA_20' in data:
649
- # # fig.add_trace(go.Scatter(
650
- # # x=data.index, y=data['MA_20'],
651
- # # name='20-day MA',
652
- # # line=dict(color='#f39c12', width=1.5, dash='dash'),
653
- # # hovertemplate='20-day MA: $%{y:.2f}<extra></extra>'
654
- # # ))
655
-
656
- # # if 'MA_50' in data:
657
- # # fig.add_trace(go.Scatter(
658
- # # x=data.index, y=data['MA_50'],
659
- # # name='50-day MA',
660
- # # line=dict(color='#e74c3c', width=1.5, dash='dash'),
661
- # # hovertemplate='50-day MA: $%{y:.2f}<extra></extra>'
662
- # # ))
663
-
664
- # # if 'BB_Upper' in data and 'BB_Lower' in data:
665
- # # fig.add_trace(go.Scatter(
666
- # # x=data.index, y=data['BB_Upper'],
667
- # # name='Upper BB',
668
- # # line=dict(color='#95a5a6', width=1, dash='dot'),
669
- # # opacity=0.7
670
- # # ))
671
- # # fig.add_trace(go.Scatter(
672
- # # x=data.index, y=data['BB_Lower'],
673
- # # name='Lower BB',
674
- # # line=dict(color='#95a5a6', width=1, dash='dot'),
675
- # # fill='tonexty',
676
- # # fillcolor='rgba(200,200,200,0.2)',
677
- # # opacity=0.7
678
- # # ))
679
-
680
- # # fig.update_layout(
681
- # # title=f'{ticker} Price Analysis {"(Simulated)" if is_dummy else ""}',
682
- # # xaxis_title='Date',
683
- # # yaxis_title='Price ($)',
684
- # # hovermode='x unified',
685
- # # template='plotly_white',
686
- # # height=500,
687
- # # margin=dict(l=50, r=50, b=50, t=80),
688
- # # legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1)
689
- # # )
690
- # # return fig
691
-
692
- # # def create_rsi_chart(data: pd.DataFrame, ticker: str) -> go.Figure:
693
- # # """Create RSI chart with overbought/oversold markers"""
694
- # # fig = go.Figure()
695
-
696
- # # fig.add_trace(go.Scatter(
697
- # # x=data.index, y=data['RSI'],
698
- # # name='RSI',
699
- # # line=dict(color='#9b59b6', width=2),
700
- # # hovertemplate='RSI: %{y:.1f}<extra></extra>'
701
- # # ))
702
-
703
- # # # Add overbought/oversold lines
704
- # # fig.add_hline(y=70, line_dash="dash", line_color="#e74c3c", annotation_text="Overbought")
705
- # # fig.add_hline(y=30, line_dash="dash", line_color="#2ecc71", annotation_text="Oversold")
706
-
707
- # # fig.update_layout(
708
- # # title=f'{ticker} RSI (14-day)',
709
- # # xaxis_title='Date',
710
- # # yaxis_title='RSI',
711
- # # hovermode='x unified',
712
- # # template='plotly_white',
713
- # # height=300
714
- # # )
715
- # # return fig
716
-
717
- # # # === Main Application ===
718
- # # def main():
719
- # # st.title("πŸ“Š AI Stock Analyst Pro+")
720
- # # st.markdown("""
721
- # # <div class="header">
722
- # # Professional stock analysis with AI-powered insights and robust fallback systems
723
- # # </div>
724
- # # """, unsafe_allow_html=True)
725
-
726
- # # # Sidebar controls
727
- # # with st.sidebar:
728
- # # st.header("βš™οΈ Analysis Settings")
729
- # # default_ticker = st.text_input("Default Ticker", value="AAPL")
730
- # # period_options = ["1d", "5d", "1mo", "3mo", "6mo", "1y", "2y", "5y"]
731
- # # selected_period = st.selectbox("Time Period", period_options, index=2)
732
-
733
- # # st.markdown("---")
734
- # # st.markdown("### πŸ“Š Technical Indicators")
735
- # # show_rsi = st.checkbox("Show RSI", value=True)
736
- # # show_macd = st.checkbox("Show MACD", value=True)
737
- # # show_bb = st.checkbox("Show Bollinger Bands", value=True)
738
-
739
- # # st.markdown("---")
740
- # # st.markdown("""
741
- # # **πŸ“Œ How to use:**
742
- # # 1. Enter a stock ticker (e.g., AAPL, MSFT)
743
- # # 2. View live or simulated data
744
- # # 3. Get AI-powered insights
745
- # # """)
746
-
747
- # # # Main analysis flow
748
- # # query = st.text_input("Enter stock ticker or question",
749
- # # value=f"Analyze {default_ticker}",
750
- # # help="Try: 'Analyze AAPL technicals' or 'Should I buy TSLA?'")
751
-
752
- # # if st.button("Run Analysis", type="primary", use_container_width=True):
753
- # # # Extract ticker from query
754
- # # ticker = default_ticker
755
- # # words = query.upper().split()
756
- # # possible_tickers = [word for word in words if word.isalpha() and len(word) <= 5]
757
- # # if possible_tickers:
758
- # # ticker = possible_tickers[-1]
759
-
760
- # # with st.spinner(f"πŸ” Analyzing {ticker} (this may take a moment)..."):
761
- # # try:
762
- # # # Get data with fallback to dummy data
763
- # # data, error_msg = get_live_data(ticker, selected_period)
764
- # # is_dummy = error_msg is not None
765
-
766
- # # # Display status
767
- # # if is_dummy:
768
- # # st.warning(error_msg)
769
- # # else:
770
- # # st.success("βœ… Successfully fetched market data")
771
-
772
- # # # Create tabs for different views
773
- # # tab1, tab2, tab3 = st.tabs(["πŸ“ˆ Price Analysis", "πŸ“‹ Data Table", "🧠 AI Insights"])
774
-
775
- # # with tab1:
776
- # # # Price chart
777
- # # st.plotly_chart(create_price_chart(data, ticker, is_dummy),
778
- # # use_container_width=True)
779
-
780
- # # # Indicators in columns
781
- # # if show_rsi or show_macd:
782
- # # col1, col2 = st.columns(2)
783
-
784
- # # with col1:
785
- # # if show_rsi and 'RSI' in data:
786
- # # st.plotly_chart(create_rsi_chart(data, ticker),
787
- # # use_container_width=True)
788
-
789
- # # with col2:
790
- # # if show_macd and 'MACD' in data:
791
- # # st.plotly_chart(create_macd_chart(data, ticker),
792
- # # use_container_width=True)
793
-
794
- # # # Key metrics
795
- # # st.subheader("πŸ”‘ Key Metrics")
796
- # # cols = st.columns(4)
797
-
798
- # # with cols[0]:
799
- # # st.markdown('<div class="metric-box">', unsafe_allow_html=True)
800
- # # if 'Close' in data:
801
- # # last_close = data['Close'].iloc[-1]
802
- # # st.metric("Current Price", f"${last_close:.2f}")
803
- # # else:
804
- # # st.metric("Current Price", "N/A")
805
- # # st.markdown('</div>', unsafe_allow_html=True)
806
-
807
- # # with cols[1]:
808
- # # st.markdown('<div class="metric-box">', unsafe_allow_html=True)
809
- # # if 'RSI' in data:
810
- # # last_rsi = data['RSI'].iloc[-1]
811
- # # status = ("Overbought" if last_rsi > 70
812
- # # else "Oversold" if last_rsi < 30
813
- # # else "Neutral")
814
- # # st.metric("RSI (14-day)", f"{last_rsi:.1f}", status)
815
- # # else:
816
- # # st.metric("RSI (14-day)", "N/A")
817
- # # st.markdown('</div>', unsafe_allow_html=True)
818
-
819
- # # with cols[2]:
820
- # # st.markdown('<div class="metric-box">', unsafe_allow_html=True)
821
- # # if 'MA_20' in data and 'Close' in data:
822
- # # ma_20 = data['MA_20'].iloc[-1]
823
- # # change = (data['Close'].iloc[-1] - ma_20)/ma_20*100
824
- # # st.metric("20-day MA", f"${ma_20:.2f}", f"{change:.2f}%")
825
- # # else:
826
- # # st.metric("20-day MA", "N/A")
827
- # # st.markdown('</div>', unsafe_allow_html=True)
828
-
829
- # # with cols[3]:
830
- # # st.markdown('<div class="metric-box">', unsafe_allow_html=True)
831
- # # if 'MA_50' in data and 'Close' in data:
832
- # # ma_50 = data['MA_50'].iloc[-1]
833
- # # change = (data['Close'].iloc[-1] - ma_50)/ma_50*100
834
- # # st.metric("50-day MA", f"${ma_50:.2f}", f"{change:.2f}%")
835
- # # else:
836
- # # st.metric("50-day MA", "N/A")
837
- # # st.markdown('</div>', unsafe_allow_html=True)
838
-
839
- # # with tab2:
840
- # # # Data table view
841
- # # st.subheader("πŸ“Š Market Data")
842
-
843
- # # if not data.empty:
844
- # # display_data = data.tail(20).copy()
845
- # # display_data.index = display_data.index.strftime('%Y-%m-%d')
846
-
847
- # # # Select available columns to display
848
- # # available_cols = [col for col in ['Open', 'High', 'Low', 'Close', 'Volume',
849
- # # 'RSI', 'MA_20', 'MA_50'] if col in data]
850
-
851
- # # st.dataframe(
852
- # # display_data[available_cols].style.format("{:.2f}"),
853
- # # use_container_width=True,
854
- # # height=600
855
- # # )
856
- # # else:
857
- # # st.warning("No data available to display")
858
-
859
- # # with tab3:
860
- # # # AI analysis
861
- # # st.subheader("πŸ€– AI-Powered Analysis")
862
- # # analysis = analyze_stock_with_gpt(query, ticker, data, error_msg)
863
- # # st.markdown(analysis)
864
-
865
- # # except Exception as e:
866
- # # st.error(f"Analysis failed: {str(e)}")
867
- # # st.markdown("""
868
- # # <div class="error-box">
869
- # # <h4>🚨 Critical Error</h4>
870
- # # <p>The analysis system encountered an unexpected error. Please try again later.</p>
871
- # # </div>
872
- # # """, unsafe_allow_html=True)
873
-
874
- # # if __name__ == "__main__":
875
- # # main()
876
-
877
-
878
-
879
-
880
-
881
-
882
-
883
-
884
- # import streamlit as st
885
- # import yfinance as yf
886
- # import pandas as pd
887
- # import plotly.graph_objects as go
888
- # from datetime import datetime, timedelta
889
- # from openai import OpenAI
890
- # import numpy as np
891
- # from ta.momentum import RSIIndicator
892
- # from ta.trend import MACD, SMAIndicator
893
- # from ta.volatility import BollingerBands
894
- # import os
895
- # from dotenv import load_dotenv
896
- # import random
897
- # import time
898
- # from typing import Tuple, Optional
899
-
900
- # # Load environment variables
901
- # load_dotenv()
902
-
903
- # # Configuration
904
- # MAX_RETRIES = 3
905
- # RETRY_DELAY = 2
906
- # CACHE_EXPIRY = 3600 # 1 hour cache
907
-
908
- # # Initialize OpenAI client securely
909
- # def init_openai_client():
910
- # """Initialize OpenAI client with proper error handling"""
911
- # api_key = os.getenv("OPENAI_API_KEY")
912
- # if not api_key:
913
- # st.error("OpenAI API key not found. Please create a .env file with your key.")
914
- # return None
915
-
916
- # try:
917
- # client = OpenAI(api_key="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")
918
- # # Test the connection
919
- # client.models.list()
920
- # return client
921
- # except Exception as e:
922
- # st.error(f"Failed to initialize OpenAI: {str(e)}")
923
- # return None
924
-
925
- # client = init_openai_client()
926
-
927
- # # Page configuration
928
- # st.set_page_config(
929
- # page_title="πŸ“Š AI Stock Analyst Pro",
930
- # page_icon="πŸ“Š",
931
- # layout="wide",
932
- # initial_sidebar_state="expanded"
933
- # )
934
-
935
- # # Custom CSS for modern styling
936
- # st.markdown("""
937
- # <style>
938
- # .stApp {
939
- # background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
940
- # font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
941
- # }
942
- # .header-title {
943
- # color: #2c3e50;
944
- # text-shadow: 1px 1px 3px rgba(0,0,0,0.1);
945
- # margin-bottom: 0.5rem;
946
- # }
947
- # .metric-card {
948
- # background: white;
949
- # border-radius: 10px;
950
- # padding: 1rem;
951
- # box-shadow: 0 4px 6px rgba(0,0,0,0.1);
952
- # transition: all 0.3s ease;
953
- # border-left: 4px solid #3498db;
954
- # }
955
- # .metric-card:hover {
956
- # transform: translateY(-3px);
957
- # box-shadow: 0 6px 12px rgba(0,0,0,0.15);
958
- # }
959
- # .stTabs [aria-selected="true"] {
960
- # background: #3498db !important;
961
- # color: white !important;
962
- # font-weight: 600;
963
- # }
964
- # .stButton>button {
965
- # background: linear-gradient(90deg, #3498db 0%, #2980b9 100%);
966
- # color: white;
967
- # border: none;
968
- # border-radius: 8px;
969
- # padding: 0.75rem 1.5rem;
970
- # font-weight: 600;
971
- # transition: all 0.3s;
972
- # }
973
- # .stButton>button:hover {
974
- # transform: translateY(-2px);
975
- # box-shadow: 0 4px 8px rgba(0,0,0,0.2);
976
- # }
977
- # .warning-banner {
978
- # background-color: #fff3cd;
979
- # color: #856404;
980
- # padding: 0.75rem;
981
- # border-radius: 4px;
982
- # border-left: 4px solid #ffc107;
983
- # margin-bottom: 1rem;
984
- # }
985
- # .error-banner {
986
- # background-color: #f8d7da;
987
- # color: #721c24;
988
- # padding: 0.75rem;
989
- # border-radius: 4px;
990
- # border-left: 4px solid #dc3545;
991
- # margin-bottom: 1rem;
992
- # }
993
- # </style>
994
- # """, unsafe_allow_html=True)
995
-
996
- # # === Core Data Functions ===
997
- # def generate_dummy_data(ticker: str, days: int = 30) -> Tuple[pd.DataFrame, str]:
998
- # """Generate realistic dummy stock data with technical indicators"""
999
- # try:
1000
- # dates = pd.date_range(end=datetime.today(), periods=days)
1001
- # base_price = random.uniform(50, 200)
1002
- # volatility = random.uniform(1.5, 4.0)
1003
-
1004
- # prices = []
1005
- # trend = random.choice([-1, 1]) * random.uniform(0.1, 0.3)
1006
-
1007
- # for i in range(days):
1008
- # # Add trend and random fluctuation
1009
- # change = trend + random.uniform(-volatility, volatility)
1010
- # new_price = base_price * (1 + change/100)
1011
- # prices.append(max(1.0, new_price))
1012
- # base_price = prices[-1]
1013
-
1014
- # data = pd.DataFrame({
1015
- # 'Open': [p * random.uniform(0.99, 1.01) for p in prices],
1016
- # 'High': [p * random.uniform(1.00, 1.02) for p in prices],
1017
- # 'Low': [p * random.uniform(0.98, 1.00) for p in prices],
1018
- # 'Close': prices,
1019
- # 'Volume': [int(abs(random.gauss(2500000, 1000000))) for _ in prices]
1020
- # }, index=dates)
1021
-
1022
- # # Calculate indicators
1023
- # data = calculate_technical_indicators(data)
1024
-
1025
- # return data, f"⚠️ Using simulated data for {ticker}"
1026
- # except Exception as e:
1027
- # st.error(f"Dummy data generation failed: {str(e)}")
1028
- # return pd.DataFrame({'Close': [100]}, index=[datetime.today()]), "⚠️ Basic fallback data"
1029
-
1030
- # def calculate_technical_indicators(data: pd.DataFrame) -> pd.DataFrame:
1031
- # """Calculate all technical indicators for a dataframe"""
1032
- # try:
1033
- # # RSI (14-day)
1034
- # if len(data) >= 14:
1035
- # data["RSI"] = RSIIndicator(data["Close"], window=14).rsi()
1036
-
1037
- # # Moving Averages
1038
- # if len(data) >= 20:
1039
- # data["MA_20"] = SMAIndicator(data["Close"], window=20).sma_indicator()
1040
- # if len(data) >= 50:
1041
- # data["MA_50"] = SMAIndicator(data["Close"], window=50).sma_indicator()
1042
-
1043
- # # MACD
1044
- # if len(data) >= 26:
1045
- # macd = MACD(data["Close"])
1046
- # data["MACD"] = macd.macd()
1047
- # data["MACD_Signal"] = macd.macd_signal()
1048
- # data["MACD_Hist"] = macd.macd_diff()
1049
-
1050
- # # Bollinger Bands
1051
- # if len(data) >= 20:
1052
- # bb = BollingerBands(data["Close"])
1053
- # data["BB_Upper"] = bb.bollinger_hband()
1054
- # data["BB_Lower"] = bb.bollinger_lband()
1055
-
1056
- # return data
1057
- # except Exception as e:
1058
- # st.error(f"Technical indicator calculation failed: {str(e)}")
1059
- # return data
1060
-
1061
- # def get_live_data(ticker: str, period: str = "1mo") -> Tuple[pd.DataFrame, Optional[str]]:
1062
- # """Fetch live stock data with retry logic and fallback"""
1063
- # for attempt in range(MAX_RETRIES):
1064
- # try:
1065
- # stock = yf.Ticker(ticker)
1066
- # hist = stock.history(period=period)
1067
-
1068
- # if hist.empty:
1069
- # raise ValueError(f"No data found for {ticker}")
1070
-
1071
- # hist = calculate_technical_indicators(hist)
1072
- # return hist, None
1073
-
1074
- # except Exception as e:
1075
- # if attempt == MAX_RETRIES - 1:
1076
- # st.warning(f"Live data fetch failed, using simulated data: {str(e)}")
1077
- # return generate_dummy_data(ticker)
1078
- # time.sleep(RETRY_DELAY)
1079
-
1080
- # # === AI Analysis Functions ===
1081
- # def analyze_stock_with_gpt(query: str, ticker: Optional[str] = None,
1082
- # data: Optional[pd.DataFrame] = None,
1083
- # error_msg: Optional[str] = None) -> str:
1084
- # """Robust stock analysis with fallback options"""
1085
- # fallback_analysis = f"""
1086
- # ## πŸ“Š Manual Analysis Report for {ticker if ticker else 'this stock'}
1087
- # "gived anbalize the stock"
1088
- # **Market Overview**
1089
- # - Current conditions show {random.choice(['moderate', 'high', 'low'])} volatility
1090
- # - Sector appears {random.choice(['bullish', 'bearish', 'neutral'])}
1091
-
1092
- # **Technical Assessment**
1093
- # - Momentum indicators suggest {random.choice(['upward', 'downward', 'sideways'])} trend
1094
- # - Volume patterns indicate {random.choice(['growing', 'declining', 'stable'])} interest
1095
-
1096
- # **Recommendation**
1097
- # - Action: {random.choice(['Hold', 'Buy on dips', 'Take profits'])}
1098
- # - Risk: {random.choice(['Moderate', 'High', 'Low'])}
1099
-
1100
- # *Note: {error_msg or 'AI service unavailable'}*
1101
- # """
1102
-
1103
- # if client is None:
1104
- # return fallback_analysis
1105
-
1106
- # try:
1107
- # # Prepare context
1108
- # context = {
1109
- # "query": query,
1110
- # "ticker": ticker,
1111
- # "error_msg": error_msg,
1112
- # "data_summary": data.describe().to_dict() if data is not None else None,
1113
- # "recent_data": data.tail().to_dict() if data is not None else None
1114
- # }
1115
-
1116
- # prompt = f"""
1117
- # As a senior financial analyst, provide:
1118
- # 1. Market context summary
1119
- # 2. Technical analysis (RSI, MACD, Moving Averages)
1120
- # 3. Investment recommendation with rationale
1121
- # 4. Risk assessment
1122
-
1123
- # Context: {context}
1124
- # """
1125
-
1126
- # for attempt in range(MAX_RETRIES):
1127
- # try:
1128
- # response = client.chat.completions.create(
1129
- # model="gpt-3.5-turbo",
1130
- # messages=[
1131
- # {"role": "system", "content": "You are a professional stock analyst. Provide clear, actionable insights."},
1132
- # {"role": "user", "content": prompt}
1133
- # ],
1134
- # temperature=0.6,
1135
- # timeout=15
1136
- # )
1137
- # return response.choices[0].message.content
1138
- # except Exception as e:
1139
- # if attempt == MAX_RETRIES - 1:
1140
- # st.warning(f"AI analysis failed after retries: {str(e)}")
1141
- # return fallback_analysis
1142
- # time.sleep(RETRY_DELAY)
1143
-
1144
- # except Exception as e:
1145
- # st.error(f"Analysis system error: {str(e)}")
1146
- # return fallback_analysis
1147
-
1148
- # # === Visualization Functions ===
1149
- # def create_price_chart(data: pd.DataFrame, ticker: str, is_dummy: bool = False) -> go.Figure:
1150
- # """Create interactive price chart with indicators"""
1151
- # fig = go.Figure()
1152
-
1153
- # # Price line with conditional styling
1154
- # line_color = '#FF6B6B' if is_dummy else '#3498db'
1155
- # fig.add_trace(go.Scatter(
1156
- # x=data.index, y=data['Close'],
1157
- # name='Price',
1158
- # line=dict(color=line_color, width=2),
1159
- # hovertemplate='Date: %{x}<br>Price: $%{y:.2f}<extra></extra>'
1160
- # ))
1161
-
1162
- # # Add available indicators
1163
- # if 'MA_20' in data:
1164
- # fig.add_trace(go.Scatter(
1165
- # x=data.index, y=data['MA_20'],
1166
- # name='20-day MA',
1167
- # line=dict(color='#f39c12', width=1.5, dash='dash')
1168
- # ))
1169
-
1170
- # if 'MA_50' in data:
1171
- # fig.add_trace(go.Scatter(
1172
- # x=data.index, y=data['MA_50'],
1173
- # name='50-day MA',
1174
- # line=dict(color='#e74c3c', width=1.5, dash='dash')
1175
- # ))
1176
-
1177
- # if all(col in data for col in ['BB_Upper', 'BB_Lower']):
1178
- # fig.add_trace(go.Scatter(
1179
- # x=data.index, y=data['BB_Upper'],
1180
- # name='Upper Band',
1181
- # line=dict(color='#95a5a6', width=1, dash='dot'),
1182
- # opacity=0.7
1183
- # ))
1184
- # fig.add_trace(go.Scatter(
1185
- # x=data.index, y=data['BB_Lower'],
1186
- # name='Lower Band',
1187
- # line=dict(color='#95a5a6', width=1, dash='dot'),
1188
- # fill='tonexty',
1189
- # fillcolor='rgba(200,200,200,0.2)',
1190
- # opacity=0.7
1191
- # ))
1192
-
1193
- # fig.update_layout(
1194
- # title=f'{ticker} Price Analysis {"(Simulated)" if is_dummy else ""}',
1195
- # xaxis_title='Date',
1196
- # yaxis_title='Price ($)',
1197
- # hovermode='x unified',
1198
- # template='plotly_white',
1199
- # height=500,
1200
- # margin=dict(l=50, r=50, b=50, t=80)
1201
- # )
1202
- # return fig
1203
- # def create_rsi_chart(data: pd.DataFrame, ticker: str) -> go.Figure:
1204
- # """Create RSI chart with overbought/oversold markers"""
1205
- # fig = go.Figure()
1206
-
1207
- # fig.add_trace(go.Scatter(
1208
- # x=data.index, y=data['RSI'],
1209
- # name='RSI',
1210
- # line=dict(color='#9b59b6', width=2)
1211
- # ))
1212
-
1213
- # # Add threshold lines
1214
- # fig.add_hline(y=70, line_dash="dash", line_color="#e74c3c", annotation_text="Overbought")
1215
- # fig.add_hline(y=30, line_dash="dash", line_color="#2ecc71", annotation_text="Oversold")
1216
-
1217
- # fig.update_layout(
1218
- # title=f'{ticker} RSI (14-day)',
1219
- # xaxis_title='Date',
1220
- # yaxis_title='RSI Value',
1221
- # hovermode='x unified',
1222
- # template='plotly_white',
1223
- # height=300
1224
- # )
1225
- # return fig
1226
-
1227
- # # === Main Application ===
1228
- # def main():
1229
- # st.title("πŸ“Š AI Stock Analyst Pro")
1230
- # st.markdown('<p class="header-title">Professional stock analysis with AI-powered insights</p>',
1231
- # unsafe_allow_html=True)
1232
-
1233
- # # Sidebar controls
1234
- # with st.sidebar:
1235
- # st.header("βš™οΈ Settings")
1236
- # default_ticker = st.text_input("Default Ticker", value="AAPL")
1237
- # period_options = ["1d", "5d", "1mo", "3mo", "6mo", "1y", "2y", "5y"]
1238
- # selected_period = st.selectbox("Time Period", period_options, index=2)
1239
-
1240
- # st.markdown("---")
1241
- # st.markdown("### πŸ“Š Technical Indicators")
1242
- # show_rsi = st.checkbox("Show RSI", value=True)
1243
- # show_macd = st.checkbox("Show MACD", value=True)
1244
- # show_bb = st.checkbox("Show Bollinger Bands", value=True)
1245
-
1246
- # st.markdown("---")
1247
- # st.markdown("""
1248
- # **How to use:**
1249
- # 1. Enter a stock ticker
1250
- # 2. View live/simulated data
1251
- # 3. Get AI-powered analysis
1252
- # """)
1253
-
1254
- # # Main analysis flow
1255
- # query = st.text_input("Enter stock ticker or question",
1256
- # value=f"Analyze {default_ticker}",
1257
- # help="Example: 'Analyze AAPL' or 'Should I buy TSLA?'")
1258
-
1259
- # if st.button("Run Analysis", type="primary"):
1260
- # # Extract ticker from query
1261
- # words = query.upper().split()
1262
- # possible_tickers = [word for word in words if word.isalpha() and len(word) <= 5]
1263
- # ticker = possible_tickers[-1] if possible_tickers else default_ticker
1264
-
1265
- # with st.spinner(f"πŸ” Analyzing {ticker}..."):
1266
- # try:
1267
- # # Get data with fallback
1268
- # data, error_msg = get_live_data(ticker, selected_period)
1269
- # is_dummy = error_msg is not None
1270
-
1271
- # # Display status
1272
- # if is_dummy:
1273
- # st.warning(error_msg)
1274
- # else:
1275
- # st.success("βœ… Fetched market data")
1276
-
1277
- # # Create tabs
1278
- # tab1, tab2, tab3 = st.tabs(["πŸ“ˆ Charts", "πŸ“‹ Data", "🧠 Analysis"])
1279
-
1280
- # with tab1:
1281
- # # Price chart
1282
- # st.plotly_chart(create_price_chart(data, ticker, is_dummy),
1283
- # use_container_width=True)
1284
-
1285
- # # Indicators in columns
1286
- # if show_rsi or show_macd:
1287
- # col1, col2 = st.columns(2)
1288
-
1289
- # with col1:
1290
- # if show_rsi and 'RSI' in data:
1291
- # st.plotly_chart(create_rsi_chart(data, ticker),
1292
- # use_container_width=True)
1293
-
1294
- # with col2:
1295
- # if show_macd and 'MACD' in data:
1296
- # st.plotly_chart(create_macd_chart(data, ticker),
1297
- # use_container_width=True)
1298
-
1299
- # # Key metrics
1300
- # st.subheader("Key Metrics")
1301
- # cols = st.columns(4)
1302
-
1303
- # metric_data = [
1304
- # ("Price", data['Close'].iloc[-1] if 'Close' in data else None, "${:.2f}"),
1305
- # ("RSI", data['RSI'].iloc[-1] if 'RSI' in data else None, "{:.1f}"),
1306
- # ("20-day MA", data['MA_20'].iloc[-1] if 'MA_20' in data else None, "${:.2f}"),
1307
- # ("50-day MA", data['MA_50'].iloc[-1] if 'MA_50' in data else None, "${:.2f}")
1308
- # ]
1309
-
1310
- # for i, (title, value, fmt) in enumerate(metric_data):
1311
- # with cols[i]:
1312
- # st.markdown('<div class="metric-card">', unsafe_allow_html=True)
1313
- # if value is not None:
1314
- # delta = None
1315
- # if title == "RSI":
1316
- # status = ("Overbought" if value > 70
1317
- # else "Oversold" if value < 30
1318
- # else "Neutral")
1319
- # st.metric(title, fmt.format(value), status)
1320
- # elif "MA" in title:
1321
- # price = data['Close'].iloc[-1]
1322
- # ma_value = value
1323
- # change = (price - ma_value)/ma_value*100
1324
- # st.metric(title, fmt.format(ma_value), f"{change:.2f}%")
1325
- # else:
1326
- # st.metric(title, fmt.format(value))
1327
- # else:
1328
- # st.metric(title, "N/A")
1329
- # st.markdown('</div>', unsafe_allow_html=True)
1330
-
1331
- # with tab2:
1332
- # st.subheader("Market Data")
1333
- # if not data.empty:
1334
- # display_data = data.tail(20).copy()
1335
- # display_data.index = display_data.index.strftime('%Y-%m-%d')
1336
-
1337
- # cols_to_show = [col for col in ['Open', 'High', 'Low', 'Close', 'Volume',
1338
- # 'RSI', 'MA_20', 'MA_50'] if col in data]
1339
-
1340
- # st.dataframe(
1341
- # display_data[cols_to_show].style.format("{:.2f}"),
1342
- # use_container_width=True,
1343
- # height=600
1344
- # )
1345
- # else:
1346
- # st.warning("No data available")
1347
-
1348
- # with tab3:
1349
- # st.subheader("AI Analysis")
1350
- # analysis = analyze_stock_with_gpt(query, ticker, data, error_msg)
1351
- # st.markdown(analysis)
1352
-
1353
- # except Exception as e:
1354
- # st.error(f"Analysis failed: {str(e)}")
1355
- # st.markdown("""
1356
- # <div class="error-banner">
1357
- # <strong>Error:</strong> The analysis system encountered an unexpected problem.
1358
- # </div>
1359
- # """, unsafe_allow_html=True)
1360
-
1361
- # if __name__ == "__main__":
1362
- # if client: # Only run if OpenAI initialized successfully
1363
-
1364
-
1365
-
1366
-
1367
-
1368
-
1369
-
1370
-
1371
-
1372
-
1373
-
1374
-
1375
-
1376
-
1377
-
1378
-
1379
-
1380
-
1381
-
1382
-
1383
-
1384
-
1385
-
1386
-
1387
-
1388
-
1389
-
1390
-
1391
-
1392
-
1393
-
1394
-
1395
-
1396
-
1397
- # import streamlit as st
1398
- # import yfinance as yf
1399
- # import pandas as pd
1400
- # import plotly.graph_objects as go
1401
- # from datetime import datetime, timedelta
1402
- # from openai import OpenAI
1403
- # import numpy as np
1404
- # from ta.momentum import RSIIndicator
1405
- # from ta.trend import MACD, SMAIndicator
1406
- # from ta.volatility import BollingerBands
1407
- # import os
1408
- # from dotenv import load_dotenv
1409
- # import random
1410
- # import time
1411
- # from typing import Tuple, Optional
1412
-
1413
- # # Load environment variables
1414
- # load_dotenv()
1415
-
1416
- # # Configuration
1417
- # MAX_RETRIES = 3
1418
- # RETRY_DELAY = 2
1419
- # CACHE_EXPIRY = 3600 # 1 hour cache
1420
-
1421
- # # Initialize OpenAI client securely
1422
- # def init_openai_client():
1423
- # """Initialize OpenAI client with proper error handling"""
1424
- # api_key = os.getenv("OPENAI_API_KEY")
1425
- # if not api_key:
1426
- # st.error("OpenAI API key not found. Please create a .env file with your key.")
1427
- # return None
1428
-
1429
- # try:
1430
- # client = OpenAI(api_key="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")
1431
- # # Test the connection
1432
- # client.models.list()
1433
- # return client
1434
- # except Exception as e:
1435
- # st.error(f"Failed to initialize OpenAI: {str(e)}")
1436
- # return None
1437
-
1438
- # client = init_openai_client()
1439
-
1440
- # # Page configuration
1441
- # st.set_page_config(
1442
- # page_title="πŸ“Š AI Stock Analyst Pro",
1443
- # page_icon="πŸ“Š",
1444
- # layout="wide",
1445
- # initial_sidebar_state="expanded"
1446
- # )
1447
-
1448
- # # Dark theme custom CSS
1449
- # st.markdown("""
1450
- # <style>
1451
- # :root {
1452
- # --primary: #3498db;
1453
- # --background: #121212;
1454
- # --secondary-bg: #1e1e1e;
1455
- # --text: #ffffff;
1456
- # --accent: #2980b9;
1457
- # --warning: #ffc107;
1458
- # --error: #dc3545;
1459
- # }
1460
-
1461
- # .stApp {
1462
- # background-color: var(--background);
1463
- # color: var(--text);
1464
- # font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
1465
- # }
1466
-
1467
- # .header-title {
1468
- # color: var(--primary);
1469
- # text-shadow: 1px 1px 3px rgba(0,0,0,0.3);
1470
- # margin-bottom: 0.5rem;
1471
- # }
1472
-
1473
- # .metric-card {
1474
- # background-color: var(--secondary-bg);
1475
- # border-radius: 10px;
1476
- # padding: 1rem;
1477
- # box-shadow: 0 4px 6px rgba(0,0,0,0.3);
1478
- # transition: all 0.3s ease;
1479
- # border-left: 4px solid var(--primary);
1480
- # color: var(--text);
1481
- # }
1482
-
1483
- # .metric-card:hover {
1484
- # transform: translateY(-3px);
1485
- # box-shadow: 0 6px 12px rgba(0,0,0,0.4);
1486
- # }
1487
-
1488
- # .stTabs [aria-selected="true"] {
1489
- # background: var(--primary) !important;
1490
- # color: white !important;
1491
- # font-weight: 600;
1492
- # }
1493
-
1494
- # .stButton>button {
1495
- # background: linear-gradient(90deg, var(--primary) 0%, var(--accent) 100%);
1496
- # color: white;
1497
- # border: none;
1498
- # border-radius: 8px;
1499
- # padding: 0.75rem 1.5rem;
1500
- # font-weight: 600;
1501
- # transition: all 0.3s;
1502
- # }
1503
-
1504
- # .stButton>button:hover {
1505
- # transform: translateY(-2px);
1506
- # box-shadow: 0 4px 8px rgba(0,0,0,0.3);
1507
- # }
1508
-
1509
- # .warning-banner {
1510
- # background-color: rgba(255, 193, 7, 0.2);
1511
- # color: var(--warning);
1512
- # padding: 0.75rem;
1513
- # border-radius: 4px;
1514
- # border-left: 4px solid var(--warning);
1515
- # margin-bottom: 1rem;
1516
- # }
1517
-
1518
- # .error-banner {
1519
- # background-color: rgba(220, 53, 69, 0.2);
1520
- # color: var(--error);
1521
- # padding: 0.75rem;
1522
- # border-radius: 4px;
1523
- # border-left: 4px solid var(--error);
1524
- # margin-bottom: 1rem;
1525
- # }
1526
-
1527
- # /* Plotly dark theme */
1528
- # .js-plotly-plot .plotly, .js-plotly-plot .plotly div {
1529
- # background-color: var(--secondary-bg) !important;
1530
- # color: var(--text) !important;
1531
- # }
1532
-
1533
- # .modebar {
1534
- # background-color: var(--secondary-bg) !important;
1535
- # }
1536
-
1537
- # /* Text input styling */
1538
- # .stTextInput>div>div>input {
1539
- # background-color: var(--secondary-bg);
1540
- # color: var(--text);
1541
- # border: 1px solid #444;
1542
- # }
1543
-
1544
- # /* Select box styling */
1545
- # .stSelectbox>div>div>select {
1546
- # background-color: var(--secondary-bg);
1547
- # color: var(--text);
1548
- # border: 1px solid #444;
1549
- # }
1550
-
1551
- # /* Checkbox styling */
1552
- # .stCheckbox>label {
1553
- # color: var(--text);
1554
- # }
1555
-
1556
- # /* Dataframe styling */
1557
- # .dataframe {
1558
- # background-color: var(--secondary-bg) !important;
1559
- # color: var(--text) !important;
1560
- # }
1561
-
1562
- # .stDataFrame {
1563
- # background-color: var(--secondary-bg) !important;
1564
- # }
1565
-
1566
- # /* Sidebar styling */
1567
- # .css-1d391kg, .css-1oe5cao {
1568
- # background-color: var(--secondary-bg) !important;
1569
- # }
1570
- # </style>
1571
- # """, unsafe_allow_html=True)
1572
-
1573
- # # === Core Data Functions ===
1574
- # def generate_dummy_data(ticker: str, days: int = 30) -> Tuple[pd.DataFrame, str]:
1575
- # """Generate realistic dummy stock data with technical indicators"""
1576
- # try:
1577
- # dates = pd.date_range(end=datetime.today(), periods=days)
1578
- # base_price = random.uniform(50, 200)
1579
- # volatility = random.uniform(1.5, 4.0)
1580
-
1581
- # prices = []
1582
- # trend = random.choice([-1, 1]) * random.uniform(0.1, 0.3)
1583
-
1584
- # for i in range(days):
1585
- # # Add trend and random fluctuation
1586
- # change = trend + random.uniform(-volatility, volatility)
1587
- # new_price = base_price * (1 + change/100)
1588
- # prices.append(max(1.0, new_price))
1589
- # base_price = prices[-1]
1590
-
1591
- # data = pd.DataFrame({
1592
- # 'Open': [p * random.uniform(0.99, 1.01) for p in prices],
1593
- # 'High': [p * random.uniform(1.00, 1.02) for p in prices],
1594
- # 'Low': [p * random.uniform(0.98, 1.00) for p in prices],
1595
- # 'Close': prices,
1596
- # 'Volume': [int(abs(random.gauss(2500000, 1000000))) for _ in prices]
1597
- # }, index=dates)
1598
-
1599
- # # Calculate indicators
1600
- # data = calculate_technical_indicators(data)
1601
-
1602
- # return data, f"⚠️ Using simulated data for {ticker}"
1603
- # except Exception as e:
1604
- # st.error(f"Dummy data generation failed: {str(e)}")
1605
- # return pd.DataFrame({'Close': [100]}, index=[datetime.today()]), "⚠️ Basic fallback data"
1606
-
1607
- # def calculate_technical_indicators(data: pd.DataFrame) -> pd.DataFrame:
1608
- # """Calculate all technical indicators for a dataframe"""
1609
- # try:
1610
- # # RSI (14-day)
1611
- # if len(data) >= 14:
1612
- # data["RSI"] = RSIIndicator(data["Close"], window=14).rsi()
1613
-
1614
- # # Moving Averages
1615
- # if len(data) >= 20:
1616
- # data["MA_20"] = SMAIndicator(data["Close"], window=20).sma_indicator()
1617
- # if len(data) >= 50:
1618
- # data["MA_50"] = SMAIndicator(data["Close"], window=50).sma_indicator()
1619
-
1620
- # # MACD
1621
- # if len(data) >= 26:
1622
- # macd = MACD(data["Close"])
1623
- # data["MACD"] = macd.macd()
1624
- # data["MACD_Signal"] = macd.macd_signal()
1625
- # data["MACD_Hist"] = macd.macd_diff()
1626
-
1627
- # # Bollinger Bands
1628
- # if len(data) >= 20:
1629
- # bb = BollingerBands(data["Close"])
1630
- # data["BB_Upper"] = bb.bollinger_hband()
1631
- # data["BB_Lower"] = bb.bollinger_lband()
1632
-
1633
- # return data
1634
- # except Exception as e:
1635
- # st.error(f"Technical indicator calculation failed: {str(e)}")
1636
- # return data
1637
-
1638
- # def get_live_data(ticker: str, period: str = "1mo") -> Tuple[pd.DataFrame, Optional[str]]:
1639
- # """Fetch live stock data with retry logic and fallback"""
1640
- # for attempt in range(MAX_RETRIES):
1641
- # try:
1642
- # stock = yf.Ticker(ticker)
1643
- # hist = stock.history(period=period)
1644
-
1645
- # if hist.empty:
1646
- # raise ValueError(f"No data found for {ticker}")
1647
-
1648
- # hist = calculate_technical_indicators(hist)
1649
- # return hist, None
1650
-
1651
- # except Exception as e:
1652
- # if attempt == MAX_RETRIES - 1:
1653
- # st.warning(f"Live data fetch failed, using simulated data: {str(e)}")
1654
- # return generate_dummy_data(ticker)
1655
- # time.sleep(RETRY_DELAY)
1656
-
1657
- # # === AI Analysis Functions ===
1658
- # def analyze_stock_with_gpt(query: str, ticker: Optional[str] = None,
1659
- # data: Optional[pd.DataFrame] = None,
1660
- # error_msg: Optional[str] = None) -> str:
1661
- # """Robust stock analysis with fallback options"""
1662
- # fallback_analysis = f"""
1663
- # ## πŸ“Š Manual Analysis Report for {ticker if ticker else 'this stock'}
1664
-
1665
- # **Market Overview**
1666
- # - Current conditions show {random.choice(['moderate', 'high', 'low'])} volatility
1667
- # - Sector appears {random.choice(['bullish', 'bearish', 'neutral'])}
1668
-
1669
- # **Technical Assessment**
1670
- # - Momentum indicators suggest {random.choice(['upward', 'downward', 'sideways'])} trend
1671
- # - Volume patterns indicate {random.choice(['growing', 'declining', 'stable'])} interest
1672
-
1673
- # **Recommendation**
1674
- # - Action: {random.choice(['Hold', 'Buy on dips', 'Take profits'])}
1675
- # - Risk: {random.choice(['Moderate', 'High', 'Low'])}
1676
-
1677
- # *Note: {error_msg or 'AI service unavailable'}*
1678
- # """
1679
-
1680
- # if client is None:
1681
- # return fallback_analysis
1682
-
1683
- # try:
1684
- # # Prepare context
1685
- # context = {
1686
- # "query": query,
1687
- # "ticker": ticker,
1688
- # "error_msg": error_msg,
1689
- # "data_summary": data.describe().to_dict() if data is not None else None,
1690
- # "recent_data": data.tail().to_dict() if data is not None else None
1691
- # }
1692
-
1693
- # prompt = f"""
1694
- # As a senior financial analyst, provide:
1695
- # 1. Market context summary
1696
- # 2. Technical analysis (RSI, MACD, Moving Averages)
1697
- # 3. Investment recommendation with rationale
1698
- # 4. Risk assessment
1699
-
1700
- # Context: {context}
1701
- # """
1702
-
1703
- # for attempt in range(MAX_RETRIES):
1704
- # try:
1705
- # response = client.chat.completions.create(
1706
- # model="gpt-3.5-turbo",
1707
- # messages=[
1708
- # {"role": "system", "content": "You are a professional stock analyst. Provide clear, actionable insights."},
1709
- # {"role": "user", "content": prompt}
1710
- # ],
1711
- # temperature=0.6,
1712
- # timeout=15
1713
- # )
1714
- # return response.choices[0].message.content
1715
- # except Exception as e:
1716
- # if attempt == MAX_RETRIES - 1:
1717
- # st.warning(f"AI analysis failed after retries: {str(e)}")
1718
- # return fallback_analysis
1719
- # time.sleep(RETRY_DELAY)
1720
-
1721
- # except Exception as e:
1722
- # st.error(f"Analysis system error: {str(e)}")
1723
- # return fallback_analysis
1724
-
1725
- # # === Visualization Functions ===
1726
- # def create_price_chart(data: pd.DataFrame, ticker: str, is_dummy: bool = False) -> go.Figure:
1727
- # """Create interactive price chart with indicators"""
1728
- # fig = go.Figure()
1729
-
1730
- # # Price line with conditional styling
1731
- # line_color = '#FF6B6B' if is_dummy else '#3498db'
1732
- # fig.add_trace(go.Scatter(
1733
- # x=data.index, y=data['Close'],
1734
- # name='Price',
1735
- # line=dict(color=line_color, width=2),
1736
- # hovertemplate='Date: %{x}<br>Price: $%{y:.2f}<extra></extra>'
1737
- # ))
1738
-
1739
- # # Add available indicators
1740
- # if 'MA_20' in data:
1741
- # fig.add_trace(go.Scatter(
1742
- # x=data.index, y=data['MA_20'],
1743
- # name='20-day MA',
1744
- # line=dict(color='#f39c12', width=1.5, dash='dash')
1745
- # ))
1746
-
1747
- # if 'MA_50' in data:
1748
- # fig.add_trace(go.Scatter(
1749
- # x=data.index, y=data['MA_50'],
1750
- # name='50-day MA',
1751
- # line=dict(color='#e74c3c', width=1.5, dash='dash')
1752
- # ))
1753
-
1754
- # if all(col in data for col in ['BB_Upper', 'BB_Lower']):
1755
- # fig.add_trace(go.Scatter(
1756
- # x=data.index, y=data['BB_Upper'],
1757
- # name='Upper Band',
1758
- # line=dict(color='#95a5a6', width=1, dash='dot'),
1759
- # opacity=0.7
1760
- # ))
1761
- # fig.add_trace(go.Scatter(
1762
- # x=data.index, y=data['BB_Lower'],
1763
- # name='Lower Band',
1764
- # line=dict(color='#95a5a6', width=1, dash='dot'),
1765
- # fill='tonexty',
1766
- # fillcolor='rgba(200,200,200,0.2)',
1767
- # opacity=0.7
1768
- # ))
1769
-
1770
- # fig.update_layout(
1771
- # title=f'{ticker} Price Analysis {"(Simulated)" if is_dummy else ""}',
1772
- # xaxis_title='Date',
1773
- # yaxis_title='Price ($)',
1774
- # hovermode='x unified',
1775
- # template='plotly_dark', # Changed to dark theme
1776
- # height=500,
1777
- # margin=dict(l=50, r=50, b=50, t=80),
1778
- # plot_bgcolor='#1e1e1e',
1779
- # paper_bgcolor='#121212',
1780
- # font=dict(color='white')
1781
- # )
1782
- # return fig
1783
-
1784
- # def create_rsi_chart(data: pd.DataFrame, ticker: str) -> go.Figure:
1785
- # """Create RSI chart with overbought/oversold markers"""
1786
- # fig = go.Figure()
1787
-
1788
- # fig.add_trace(go.Scatter(
1789
- # x=data.index, y=data['RSI'],
1790
- # name='RSI',
1791
- # line=dict(color='#9b59b6', width=2)
1792
- # ))
1793
-
1794
- # # Add threshold lines
1795
- # fig.add_hline(y=70, line_dash="dash", line_color="#e74c3c", annotation_text="Overbought")
1796
- # fig.add_hline(y=30, line_dash="dash", line_color="#2ecc71", annotation_text="Oversold")
1797
-
1798
- # fig.update_layout(
1799
- # title=f'{ticker} RSI (14-day)',
1800
- # xaxis_title='Date',
1801
- # yaxis_title='RSI Value',
1802
- # hovermode='x unified',
1803
- # template='plotly_dark', # Changed to dark theme
1804
- # height=300,
1805
- # plot_bgcolor='#1e1e1e',
1806
- # paper_bgcolor='#121212',
1807
- # font=dict(color='white')
1808
- # )
1809
- # return fig
1810
-
1811
- # def create_macd_chart(data: pd.DataFrame, ticker: str) -> go.Figure:
1812
- # """Create MACD chart"""
1813
- # fig = go.Figure()
1814
-
1815
- # if 'MACD' in data:
1816
- # fig.add_trace(go.Scatter(
1817
- # x=data.index, y=data['MACD'],
1818
- # name='MACD',
1819
- # line=dict(color='#3498db', width=2)
1820
- # ))
1821
-
1822
- # if 'MACD_Signal' in data:
1823
- # fig.add_trace(go.Scatter(
1824
- # x=data.index, y=data['MACD_Signal'],
1825
- # name='Signal',
1826
- # line=dict(color='#f39c12', width=2)
1827
- # ))
1828
-
1829
- # if 'MACD_Hist' in data:
1830
- # colors = np.where(data['MACD_Hist'] < 0, '#e74c3c', '#2ecc71')
1831
- # fig.add_trace(go.Bar(
1832
- # x=data.index, y=data['MACD_Hist'],
1833
- # name='Histogram',
1834
- # marker_color=colors,
1835
- # opacity=0.6
1836
- # ))
1837
-
1838
- # fig.update_layout(
1839
- # title=f'{ticker} MACD',
1840
- # xaxis_title='Date',
1841
- # yaxis_title='Value',
1842
- # hovermode='x unified',
1843
- # template='plotly_dark', # Changed to dark theme
1844
- # height=300,
1845
- # plot_bgcolor='#1e1e1e',
1846
- # paper_bgcolor='#121212',
1847
- # font=dict(color='white')
1848
- # )
1849
- # return fig
1850
-
1851
- # # === Main Application ===
1852
- # def main():
1853
- # st.title("πŸ“Š AI Stock Analyst Pro")
1854
- # st.markdown('<p class="header-title">Professional stock analysis with AI-powered insights</p>',
1855
- # unsafe_allow_html=True)
1856
-
1857
- # # Sidebar controls
1858
- # with st.sidebar:
1859
- # st.header("βš™οΈ Settings")
1860
- # default_ticker = st.text_input("Default Ticker", value="AAPL")
1861
- # period_options = ["1d", "5d", "1mo", "3mo", "6mo", "1y", "2y", "5y"]
1862
- # selected_period = st.selectbox("Time Period", period_options, index=2)
1863
-
1864
- # st.markdown("---")
1865
- # st.markdown("### πŸ“Š Technical Indicators")
1866
- # show_rsi = st.checkbox("Show RSI", value=True)
1867
- # show_macd = st.checkbox("Show MACD", value=True)
1868
- # show_bb = st.checkbox("Show Bollinger Bands", value=True)
1869
-
1870
- # st.markdown("---")
1871
- # st.markdown("""
1872
- # **How to use:**
1873
- # 1. Enter a stock ticker
1874
- # 2. View live/simulated data
1875
- # 3. Get AI-powered analysis
1876
- # """)
1877
-
1878
- # # Main analysis flow
1879
- # query = st.text_input("Enter stock ticker or question",
1880
- # value=f"Analyze {default_ticker}",
1881
- # help="Example: 'Analyze AAPL' or 'Should I buy TSLA?'")
1882
-
1883
- # if st.button("Run Analysis", type="primary"):
1884
- # # Extract ticker from query
1885
- # words = query.upper().split()
1886
- # possible_tickers = [word for word in words if word.isalpha() and len(word) <= 5]
1887
- # ticker = possible_tickers[-1] if possible_tickers else default_ticker
1888
-
1889
- # with st.spinner(f"πŸ” Analyzing {ticker}..."):
1890
- # try:
1891
- # # Get data with fallback
1892
- # data, error_msg = get_live_data(ticker, selected_period)
1893
- # is_dummy = error_msg is not None
1894
-
1895
- # # Display status
1896
- # if is_dummy:
1897
- # st.warning(error_msg)
1898
- # else:
1899
- # st.success("βœ… Fetched market data")
1900
-
1901
- # # Create tabs
1902
- # tab1, tab2, tab3 = st.tabs(["πŸ“ˆ Charts", "πŸ“‹ Data", "🧠 Analysis"])
1903
-
1904
- # with tab1:
1905
- # # Price chart
1906
- # st.plotly_chart(create_price_chart(data, ticker, is_dummy),
1907
- # use_container_width=True)
1908
-
1909
- # # Indicators in columns
1910
- # if show_rsi or show_macd:
1911
- # col1, col2 = st.columns(2)
1912
-
1913
- # with col1:
1914
- # if show_rsi and 'RSI' in data:
1915
- # st.plotly_chart(create_rsi_chart(data, ticker),
1916
- # use_container_width=True)
1917
-
1918
- # with col2:
1919
- # if show_macd and 'MACD' in data:
1920
- # st.plotly_chart(create_macd_chart(data, ticker),
1921
- # use_container_width=True)
1922
-
1923
- # # Key metrics
1924
- # st.subheader("Key Metrics")
1925
- # cols = st.columns(4)
1926
-
1927
- # metric_data = [
1928
- # ("Price", data['Close'].iloc[-1] if 'Close' in data else None, "${:.2f}"),
1929
- # ("RSI", data['RSI'].iloc[-1] if 'RSI' in data else None, "{:.1f}"),
1930
- # ("20-day MA", data['MA_20'].iloc[-1] if 'MA_20' in data else None, "${:.2f}"),
1931
- # ("50-day MA", data['MA_50'].iloc[-1] if 'MA_50' in data else None, "${:.2f}")
1932
- # ]
1933
-
1934
- # for i, (title, value, fmt) in enumerate(metric_data):
1935
- # with cols[i]:
1936
- # st.markdown('<div class="metric-card">', unsafe_allow_html=True)
1937
- # if value is not None:
1938
- # delta = None
1939
- # if title == "RSI":
1940
- # status = ("Overbought" if value > 70
1941
- # else "Oversold" if value < 30
1942
- # else "Neutral")
1943
- # st.metric(title, fmt.format(value), status)
1944
- # elif "MA" in title:
1945
- # price = data['Close'].iloc[-1]
1946
- # ma_value = value
1947
- # change = (price - ma_value)/ma_value*100
1948
- # st.metric(title, fmt.format(ma_value), f"{change:.2f}%")
1949
- # else:
1950
- # st.metric(title, fmt.format(value))
1951
- # else:
1952
- # st.metric(title, "N/A")
1953
- # st.markdown('</div>', unsafe_allow_html=True)
1954
-
1955
- # with tab2:
1956
- # st.subheader("Market Data")
1957
- # if not data.empty:
1958
- # display_data = data.tail(20).copy()
1959
- # display_data.index = display_data.index.strftime('%Y-%m-%d')
1960
-
1961
- # cols_to_show = [col for col in ['Open', 'High', 'Low', 'Close', 'Volume',
1962
- # 'RSI', 'MA_20', 'MA_50'] if col in data]
1963
-
1964
- # st.dataframe(
1965
- # display_data[cols_to_show].style.format("{:.2f}"),
1966
- # use_container_width=True,
1967
- # height=600
1968
- # )
1969
- # else:
1970
- # st.warning("No data available")
1971
-
1972
- # with tab3:
1973
- # st.subheader("AI Analysis")
1974
- # analysis = analyze_stock_with_gpt(query, ticker, data, error_msg)
1975
- # st.markdown(analysis)
1976
-
1977
- # except Exception as e:
1978
- # st.error(f"Analysis failed: {str(e)}")
1979
- # st.markdown("""
1980
- # <div class="error-banner">
1981
- # <strong>Error:</strong> The analysis system encountered an unexpected problem.
1982
- # </div>
1983
- # """, unsafe_allow_html=True)
1984
-
1985
- # if __name__ == "__main__":
1986
- # if client: # Only run if OpenAI initialized successfully
1987
- # main()
1988
-
1989
-
1990
-
1991
-
1992
-
1993
-
1994
-
1995
-
1996
  import streamlit as st
1997
  import yfinance as yf
1998
  import pandas as pd
@@ -2034,7 +39,7 @@ def init_gemini_client():
2034
 
2035
  gemini_model = init_gemini_client()
2036
 
2037
- # Page configuration (keep all your existing CSS and styling)
2038
  st.set_page_config(
2039
  page_title="πŸ“Š AI Stock Analyst Pro",
2040
  page_icon="πŸ“Š",
@@ -2051,9 +56,88 @@ st.markdown("""
2051
 
2052
  # === Core Data Functions ===
2053
  # (Keep all your existing data functions exactly the same)
2054
- # generate_dummy_data()
2055
- # calculate_technical_indicators()
2056
- # get_live_data()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2057
 
2058
  # === AI Analysis Functions ===
2059
  def analyze_stock_with_gemini(query: str, ticker: Optional[str] = None,
@@ -2117,9 +201,130 @@ def analyze_stock_with_gemini(query: str, ticker: Optional[str] = None,
2117
 
2118
  # === Visualization Functions ===
2119
  # (Keep all your existing visualization functions exactly the same)
2120
- # create_price_chart()
2121
- # create_rsi_chart()
2122
- # create_macd_chart()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2123
 
2124
  # === Main Application ===
2125
  def main():
@@ -2127,7 +332,7 @@ def main():
2127
  st.markdown('<p class="header-title">Professional stock analysis with AI-powered insights</p>',
2128
  unsafe_allow_html=True)
2129
 
2130
- # Sidebar controls (keep your existing sidebar code)
2131
  with st.sidebar:
2132
  st.header("βš™οΈ Settings")
2133
  default_ticker = st.text_input("Default Ticker", value="AAPL")
@@ -2161,7 +366,7 @@ def main():
2161
 
2162
  with st.spinner(f"πŸ” Analyzing {ticker}..."):
2163
  try:
2164
- # Get data with fallback (keep your existing data fetching code)
2165
  data, error_msg = get_live_data(ticker, selected_period)
2166
  is_dummy = error_msg is not None
2167
 
@@ -2171,16 +376,76 @@ def main():
2171
  else:
2172
  st.success("βœ… Fetched market data")
2173
 
2174
- # Create tabs (keep your existing tab structure)
2175
  tab1, tab2, tab3 = st.tabs(["πŸ“ˆ Charts", "πŸ“‹ Data", "🧠 Analysis"])
2176
 
2177
  with tab1:
2178
- # (Keep all your existing chart display code)
2179
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2180
 
2181
  with tab2:
2182
- # (Keep all your existing data display code)
2183
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
2184
 
2185
  with tab3:
2186
  st.subheader("AI Analysis")
@@ -2197,24 +462,4 @@ def main():
2197
 
2198
  if __name__ == "__main__":
2199
  if gemini_model: # Only run if Gemini initialized successfully
2200
- main()
2201
-
2202
-
2203
-
2204
-
2205
-
2206
-
2207
-
2208
-
2209
-
2210
-
2211
-
2212
-
2213
-
2214
-
2215
-
2216
-
2217
-
2218
-
2219
-
2220
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
  import yfinance as yf
3
  import pandas as pd
 
39
 
40
  gemini_model = init_gemini_client()
41
 
42
+ # Page configuration
43
  st.set_page_config(
44
  page_title="πŸ“Š AI Stock Analyst Pro",
45
  page_icon="πŸ“Š",
 
56
 
57
  # === Core Data Functions ===
58
  # (Keep all your existing data functions exactly the same)
59
+ def generate_dummy_data(ticker: str, days: int = 30) -> Tuple[pd.DataFrame, str]:
60
+ """Generate realistic dummy stock data with technical indicators"""
61
+ try:
62
+ dates = pd.date_range(end=datetime.today(), periods=days)
63
+ base_price = random.uniform(50, 200)
64
+ volatility = random.uniform(1.5, 4.0)
65
+
66
+ prices = []
67
+ trend = random.choice([-1, 1]) * random.uniform(0.1, 0.3)
68
+
69
+ for i in range(days):
70
+ # Add trend and random fluctuation
71
+ change = trend + random.uniform(-volatility, volatility)
72
+ new_price = base_price * (1 + change/100)
73
+ prices.append(max(1.0, new_price))
74
+ base_price = prices[-1]
75
+
76
+ data = pd.DataFrame({
77
+ 'Open': [p * random.uniform(0.99, 1.01) for p in prices],
78
+ 'High': [p * random.uniform(1.00, 1.02) for p in prices],
79
+ 'Low': [p * random.uniform(0.98, 1.00) for p in prices],
80
+ 'Close': prices,
81
+ 'Volume': [int(abs(random.gauss(2500000, 1000000))) for _ in prices]
82
+ }, index=dates)
83
+
84
+ # Calculate indicators
85
+ data = calculate_technical_indicators(data)
86
+
87
+ return data, f"⚠️ Using simulated data for {ticker}"
88
+ except Exception as e:
89
+ st.error(f"Dummy data generation failed: {str(e)}")
90
+ return pd.DataFrame({'Close': [100]}, index=[datetime.today()]), "⚠️ Basic fallback data"
91
+
92
+ def calculate_technical_indicators(data: pd.DataFrame) -> pd.DataFrame:
93
+ """Calculate all technical indicators for a dataframe"""
94
+ try:
95
+ # RSI (14-day)
96
+ if len(data) >= 14:
97
+ data["RSI"] = RSIIndicator(data["Close"], window=14).rsi()
98
+
99
+ # Moving Averages
100
+ if len(data) >= 20:
101
+ data["MA_20"] = SMAIndicator(data["Close"], window=20).sma_indicator()
102
+ if len(data) >= 50:
103
+ data["MA_50"] = SMAIndicator(data["Close"], window=50).sma_indicator()
104
+
105
+ # MACD
106
+ if len(data) >= 26:
107
+ macd = MACD(data["Close"])
108
+ data["MACD"] = macd.macd()
109
+ data["MACD_Signal"] = macd.macd_signal()
110
+ data["MACD_Hist"] = macd.macd_diff()
111
+
112
+ # Bollinger Bands
113
+ if len(data) >= 20:
114
+ bb = BollingerBands(data["Close"])
115
+ data["BB_Upper"] = bb.bollinger_hband()
116
+ data["BB_Lower"] = bb.bollinger_lband()
117
+
118
+ return data
119
+ except Exception as e:
120
+ st.error(f"Technical indicator calculation failed: {str(e)}")
121
+ return data
122
+
123
+ def get_live_data(ticker: str, period: str = "1mo") -> Tuple[pd.DataFrame, Optional[str]]:
124
+ """Fetch live stock data with retry logic and fallback"""
125
+ for attempt in range(MAX_RETRIES):
126
+ try:
127
+ stock = yf.Ticker(ticker)
128
+ hist = stock.history(period=period)
129
+
130
+ if hist.empty:
131
+ raise ValueError(f"No data found for {ticker}")
132
+
133
+ hist = calculate_technical_indicators(hist)
134
+ return hist, None
135
+
136
+ except Exception as e:
137
+ if attempt == MAX_RETRIES - 1:
138
+ st.warning(f"Live data fetch failed, using simulated data: {str(e)}")
139
+ return generate_dummy_data(ticker)
140
+ time.sleep(RETRY_DELAY)
141
 
142
  # === AI Analysis Functions ===
143
  def analyze_stock_with_gemini(query: str, ticker: Optional[str] = None,
 
201
 
202
  # === Visualization Functions ===
203
  # (Keep all your existing visualization functions exactly the same)
204
+ def create_price_chart(data: pd.DataFrame, ticker: str, is_dummy: bool = False) -> go.Figure:
205
+ """Create interactive price chart with indicators"""
206
+ fig = go.Figure()
207
+
208
+ # Price line with conditional styling
209
+ line_color = '#FF6B6B' if is_dummy else '#3498db'
210
+ fig.add_trace(go.Scatter(
211
+ x=data.index, y=data['Close'],
212
+ name='Price',
213
+ line=dict(color=line_color, width=2),
214
+ hovertemplate='Date: %{x}<br>Price: $%{y:.2f}<extra></extra>'
215
+ ))
216
+
217
+ # Add available indicators
218
+ if 'MA_20' in data:
219
+ fig.add_trace(go.Scatter(
220
+ x=data.index, y=data['MA_20'],
221
+ name='20-day MA',
222
+ line=dict(color='#f39c12', width=1.5, dash='dash')
223
+ ))
224
+
225
+ if 'MA_50' in data:
226
+ fig.add_trace(go.Scatter(
227
+ x=data.index, y=data['MA_50'],
228
+ name='50-day MA',
229
+ line=dict(color='#e74c3c', width=1.5, dash='dash')
230
+ ))
231
+
232
+ if all(col in data for col in ['BB_Upper', 'BB_Lower']):
233
+ fig.add_trace(go.Scatter(
234
+ x=data.index, y=data['BB_Upper'],
235
+ name='Upper Band',
236
+ line=dict(color='#95a5a6', width=1, dash='dot'),
237
+ opacity=0.7
238
+ ))
239
+ fig.add_trace(go.Scatter(
240
+ x=data.index, y=data['BB_Lower'],
241
+ name='Lower Band',
242
+ line=dict(color='#95a5a6', width=1, dash='dot'),
243
+ fill='tonexty',
244
+ fillcolor='rgba(200,200,200,0.2)',
245
+ opacity=0.7
246
+ ))
247
+
248
+ fig.update_layout(
249
+ title=f'{ticker} Price Analysis {"(Simulated)" if is_dummy else ""}',
250
+ xaxis_title='Date',
251
+ yaxis_title='Price ($)',
252
+ hovermode='x unified',
253
+ template='plotly_dark',
254
+ height=500,
255
+ margin=dict(l=50, r=50, b=50, t=80),
256
+ plot_bgcolor='#1e1e1e',
257
+ paper_bgcolor='#121212',
258
+ font=dict(color='white')
259
+ )
260
+ return fig
261
+
262
+ def create_rsi_chart(data: pd.DataFrame, ticker: str) -> go.Figure:
263
+ """Create RSI chart with overbought/oversold markers"""
264
+ fig = go.Figure()
265
+
266
+ fig.add_trace(go.Scatter(
267
+ x=data.index, y=data['RSI'],
268
+ name='RSI',
269
+ line=dict(color='#9b59b6', width=2)
270
+ ))
271
+
272
+ # Add threshold lines
273
+ fig.add_hline(y=70, line_dash="dash", line_color="#e74c3c", annotation_text="Overbought")
274
+ fig.add_hline(y=30, line_dash="dash", line_color="#2ecc71", annotation_text="Oversold")
275
+
276
+ fig.update_layout(
277
+ title=f'{ticker} RSI (14-day)',
278
+ xaxis_title='Date',
279
+ yaxis_title='RSI Value',
280
+ hovermode='x unified',
281
+ template='plotly_dark',
282
+ height=300,
283
+ plot_bgcolor='#1e1e1e',
284
+ paper_bgcolor='#121212',
285
+ font=dict(color='white')
286
+ )
287
+ return fig
288
+
289
+ def create_macd_chart(data: pd.DataFrame, ticker: str) -> go.Figure:
290
+ """Create MACD chart"""
291
+ fig = go.Figure()
292
+
293
+ if 'MACD' in data:
294
+ fig.add_trace(go.Scatter(
295
+ x=data.index, y=data['MACD'],
296
+ name='MACD',
297
+ line=dict(color='#3498db', width=2)
298
+ ))
299
+
300
+ if 'MACD_Signal' in data:
301
+ fig.add_trace(go.Scatter(
302
+ x=data.index, y=data['MACD_Signal'],
303
+ name='Signal',
304
+ line=dict(color='#f39c12', width=2)
305
+ ))
306
+
307
+ if 'MACD_Hist' in data:
308
+ colors = np.where(data['MACD_Hist'] < 0, '#e74c3c', '#2ecc71')
309
+ fig.add_trace(go.Bar(
310
+ x=data.index, y=data['MACD_Hist'],
311
+ name='Histogram',
312
+ marker_color=colors,
313
+ opacity=0.6
314
+ ))
315
+
316
+ fig.update_layout(
317
+ title=f'{ticker} MACD',
318
+ xaxis_title='Date',
319
+ yaxis_title='Value',
320
+ hovermode='x unified',
321
+ template='plotly_dark',
322
+ height=300,
323
+ plot_bgcolor='#1e1e1e',
324
+ paper_bgcolor='#121212',
325
+ font=dict(color='white')
326
+ )
327
+ return fig
328
 
329
  # === Main Application ===
330
  def main():
 
332
  st.markdown('<p class="header-title">Professional stock analysis with AI-powered insights</p>',
333
  unsafe_allow_html=True)
334
 
335
+ # Sidebar controls
336
  with st.sidebar:
337
  st.header("βš™οΈ Settings")
338
  default_ticker = st.text_input("Default Ticker", value="AAPL")
 
366
 
367
  with st.spinner(f"πŸ” Analyzing {ticker}..."):
368
  try:
369
+ # Get data with fallback
370
  data, error_msg = get_live_data(ticker, selected_period)
371
  is_dummy = error_msg is not None
372
 
 
376
  else:
377
  st.success("βœ… Fetched market data")
378
 
379
+ # Create tabs
380
  tab1, tab2, tab3 = st.tabs(["πŸ“ˆ Charts", "πŸ“‹ Data", "🧠 Analysis"])
381
 
382
  with tab1:
383
+ # Price chart
384
+ st.plotly_chart(create_price_chart(data, ticker, is_dummy),
385
+ use_container_width=True)
386
+
387
+ # Indicators in columns
388
+ if show_rsi or show_macd:
389
+ col1, col2 = st.columns(2)
390
+
391
+ with col1:
392
+ if show_rsi and 'RSI' in data:
393
+ st.plotly_chart(create_rsi_chart(data, ticker),
394
+ use_container_width=True)
395
+
396
+ with col2:
397
+ if show_macd and 'MACD' in data:
398
+ st.plotly_chart(create_macd_chart(data, ticker),
399
+ use_container_width=True)
400
+
401
+ # Key metrics
402
+ st.subheader("Key Metrics")
403
+ cols = st.columns(4)
404
+
405
+ metric_data = [
406
+ ("Price", data['Close'].iloc[-1] if 'Close' in data else None, "${:.2f}"),
407
+ ("RSI", data['RSI'].iloc[-1] if 'RSI' in data else None, "{:.1f}"),
408
+ ("20-day MA", data['MA_20'].iloc[-1] if 'MA_20' in data else None, "${:.2f}"),
409
+ ("50-day MA", data['MA_50'].iloc[-1] if 'MA_50' in data else None, "${:.2f}")
410
+ ]
411
+
412
+ for i, (title, value, fmt) in enumerate(metric_data):
413
+ with cols[i]:
414
+ st.markdown('<div class="metric-card">', unsafe_allow_html=True)
415
+ if value is not None:
416
+ delta = None
417
+ if title == "RSI":
418
+ status = ("Overbought" if value > 70
419
+ else "Oversold" if value < 30
420
+ else "Neutral")
421
+ st.metric(title, fmt.format(value), status)
422
+ elif "MA" in title:
423
+ price = data['Close'].iloc[-1]
424
+ ma_value = value
425
+ change = (price - ma_value)/ma_value*100
426
+ st.metric(title, fmt.format(ma_value), f"{change:.2f}%")
427
+ else:
428
+ st.metric(title, fmt.format(value))
429
+ else:
430
+ st.metric(title, "N/A")
431
+ st.markdown('</div>', unsafe_allow_html=True)
432
 
433
  with tab2:
434
+ st.subheader("Market Data")
435
+ if not data.empty:
436
+ display_data = data.tail(20).copy()
437
+ display_data.index = display_data.index.strftime('%Y-%m-%d')
438
+
439
+ cols_to_show = [col for col in ['Open', 'High', 'Low', 'Close', 'Volume',
440
+ 'RSI', 'MA_20', 'MA_50'] if col in data]
441
+
442
+ st.dataframe(
443
+ display_data[cols_to_show].style.format("{:.2f}"),
444
+ use_container_width=True,
445
+ height=600
446
+ )
447
+ else:
448
+ st.warning("No data available")
449
 
450
  with tab3:
451
  st.subheader("AI Analysis")
 
462
 
463
  if __name__ == "__main__":
464
  if gemini_model: # Only run if Gemini initialized successfully
465
+ main()