DZsoul commited on
Commit
5a50d7c
·
verified ·
1 Parent(s): 36b1862

Upload 9 files

Browse files
STOCK-MARKET-APP/app.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from view_page import StockDashboard
3
+ from model_page import StockModelPage
4
+
5
+ def main():
6
+ st.set_page_config(layout='wide', page_title='Stock Analysis', page_icon=':dollar:')
7
+ page = st.sidebar.radio('Pages', ['View Page', 'Model Page'])
8
+ if page == 'View Page':
9
+ StockDashboard().run()
10
+ elif page == 'Model Page':
11
+ StockModelPage().run()
12
+
13
+ if __name__ == '__main__':
14
+ main()
STOCK-MARKET-APP/config.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Configuration settings for the stock forecasting app
2
+
3
+ # Rate limiting settings
4
+ RATE_LIMIT_DELAY = 0.5 # Minimum delay between API calls (seconds)
5
+ MAX_RETRIES = 3 # Maximum number of retries for failed requests
6
+ BASE_RETRY_DELAY = 3 # Base delay for exponential backoff (seconds)
7
+
8
+ # Cache settings
9
+ DEFAULT_CACHE_TTL = 300 # Default cache time-to-live (seconds) - 5 minutes
10
+ MODEL_CACHE_TTL = 600 # Cache TTL for model data (seconds) - 10 minutes
11
+
12
+ # API settings
13
+ YAHOO_FINANCE_TIMEOUT = 10 # Timeout for yfinance requests (seconds)
14
+
15
+ # UI settings
16
+ DEFAULT_TICKERS = ['NVDA', 'AAPL', 'GOOGL', 'MSFT', 'AMZN']
17
+ PERIOD_MAP = {
18
+ 'all': 'max',
19
+ '1m': '1mo',
20
+ '6m': '6mo',
21
+ '1y': '1y'
22
+ }
23
+
24
+ # Error messages
25
+ ERROR_MESSAGES = {
26
+ 'rate_limit': """
27
+ 🚫 **Rate Limit Exceeded**
28
+
29
+ Yahoo Finance has temporarily limited your requests. This happens when too many requests are made in a short time.
30
+
31
+ **What you can do:**
32
+ - Wait 5-10 minutes before trying again
33
+ - Use the cached data if available
34
+ - Try a different stock ticker
35
+
36
+ The app will automatically retry with delays between requests.
37
+ """,
38
+ 'network': """
39
+ 🌐 **Network Error**
40
+
41
+ There seems to be a connectivity issue.
42
+
43
+ **What you can do:**
44
+ - Check your internet connection
45
+ - Try refreshing the page
46
+ - Wait a moment and try again
47
+ """,
48
+ 'no_data': """
49
+ 📊 **No Data Available**
50
+
51
+ No stock data was found for the selected ticker and time period.
52
+
53
+ **What you can do:**
54
+ - Try a different time period
55
+ - Check if the ticker symbol is correct
56
+ - Try a different stock ticker
57
+ """
58
+ }
STOCK-MARKET-APP/mode.page.txt ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import streamlit as st
3
+ from model import Model
4
+ from plots import Plots
5
+ from stock_data_loader import StockDataLoader
6
+
7
+ class StockModelPage:
8
+ def __init__(self):
9
+ self.tickers = ['NVDA', 'AAPL', 'GOOGL', 'MSFT', 'AMZN']
10
+ self.setup_sidebar()
11
+
12
+ def setup_sidebar(self):
13
+ self.ticker = st.sidebar.selectbox('Choose Stock Ticker', self.tickers)
14
+ self.start_date = st.sidebar.date_input('Start Date', value=pd.to_datetime('2010-01-01'))
15
+ self.end_date = st.sidebar.date_input('End Date', value=pd.to_datetime('today'))
16
+ self.load_button_clicked = st.sidebar.button('Load Data')
17
+
18
+ def load_data(self):
19
+ if self.load_button_clicked:
20
+ loader = StockDataLoader(self.ticker, self.start_date, self.end_date)
21
+ st.session_state['stock_data'] = loader.get_stock_data()
22
+ st.write("--------------------------------------------")
23
+ st.write(f"Data for {self.ticker} from {self.start_date} to {self.end_date} loaded successfully!")
24
+
25
+ def handle_model_training(self):
26
+ if 'stock_data' in st.session_state:
27
+ stock_data = st.session_state['stock_data']
28
+ if st.button('Train Model'):
29
+ st.write("Training Model...")
30
+ model = Model(stock_data)
31
+ model.train_lstm()
32
+ predictions = model.make_predictions()
33
+ future_predictions = model.forecast_future(days=5)
34
+ self.plot_predictions(stock_data, predictions, future_predictions)
35
+ else:
36
+ st.write("Click the button above to train the model.")
37
+ else:
38
+ st.write("--------------------------------------------")
39
+ st.write("Please load data before training the model.")
40
+
41
+ def plot_predictions(self, stock_data, predictions, future_predictions):
42
+ plot_instance = Plots(stock_data)
43
+ plot_instance.plot_predictions(predictions, future_predictions)
44
+
45
+ def run(self):
46
+ st.write("--------------------------------------------")
47
+ st.write(f'<div style="font-size:50px">🤖 Real-Time Stock Prediction', unsafe_allow_html=True)
48
+ self.load_data()
49
+ self.handle_model_training()
STOCK-MARKET-APP/model.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from sklearn.preprocessing import MinMaxScaler
3
+ from keras.models import Sequential
4
+ from keras.layers import LSTM, Dense
5
+ import warnings
6
+ warnings.filterwarnings("ignore")
7
+
8
+ class Model:
9
+ def __init__(self, data):
10
+ self.data = data
11
+ self.scaler = MinMaxScaler(feature_range=(0, 1))
12
+ self.model = None
13
+
14
+ def prepare_data(self, look_back=1):
15
+ scaled_data = self.scaler.fit_transform(self.data['Close'].values.reshape(-1, 1))
16
+ def create_dataset(dataset):
17
+ X, Y = [], []
18
+ for i in range(len(dataset) - look_back):
19
+ a = dataset[i:(i + look_back), 0]
20
+ X.append(a)
21
+ Y.append(dataset[i + look_back, 0])
22
+ return np.array(X), np.array(Y)
23
+
24
+ X, Y = create_dataset(scaled_data)
25
+ X = np.reshape(X, (X.shape[0], 1, X.shape[1]))
26
+ return X, Y
27
+
28
+ def train_lstm(self, epochs=5, batch_size=1):
29
+ X, Y = self.prepare_data()
30
+ self.model = Sequential()
31
+ self.model.add(LSTM(50, input_shape=(1, 1)))
32
+ self.model.add(Dense(1))
33
+ self.model.compile(loss='mean_squared_error', optimizer='adam')
34
+ self.model.fit(X, Y, epochs=epochs, batch_size=batch_size, verbose=0)
35
+
36
+ def make_predictions(self):
37
+ X, _ = self.prepare_data()
38
+ predictions = self.model.predict(X)
39
+ predictions = self.scaler.inverse_transform(predictions)
40
+ return predictions
41
+
42
+ def forecast_future(self, days=5):
43
+ last_value = self.data['Close'].values[-1:].reshape(-1, 1)
44
+ last_scaled = self.scaler.transform(last_value)
45
+ future_predictions = []
46
+ for _ in range(days):
47
+ prediction = self.model.predict(last_scaled.reshape(1, 1, 1))[0]
48
+ future_predictions.append(prediction)
49
+ last_scaled = prediction
50
+ future_predictions = self.scaler.inverse_transform(future_predictions)
51
+ return future_predictions
STOCK-MARKET-APP/plots.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import streamlit as st
3
+ import plotly.graph_objects as go
4
+ from plotly.subplots import make_subplots
5
+
6
+
7
+ class StockChart:
8
+ def __init__(self, data):
9
+ self.data = data
10
+ self.fig = make_subplots(rows=2, cols=1, vertical_spacing=0.01, shared_xaxes=True)
11
+
12
+ def add_price_chart(self):
13
+ self.fig.add_trace(go.Scatter(x=self.data.index, y=self.data['Open'], name='Open Price', marker_color='#1F77B4'), row=1, col=1)
14
+ self.fig.add_trace(go.Scatter(x=self.data.index, y=self.data['High'], name='High Price', marker_color='#9467BD'), row=1, col=1)
15
+ self.fig.add_trace(go.Scatter(x=self.data.index, y=self.data['Low'], name='Low Price', marker_color='#D62728'), row=1, col=1)
16
+ self.fig.add_trace(go.Scatter(x=self.data.index, y=self.data['Close'], name='Close Price', marker_color='#76B900'), row=1, col=1)
17
+
18
+
19
+ def add_oversold_overbought_lines(self):
20
+ self.fig.add_hline(y=30, line_dash='dash', line_color='limegreen', line_width=1, row=1, col=1)
21
+ self.fig.add_hline(y=70, line_dash='dash', line_color='red', line_width=1, row=1, col=1)
22
+ self.fig.update_yaxes(title_text='RSI Score', row=1, col=1)
23
+
24
+ def add_volume_chart(self):
25
+ colors = ['#9C1F0B' if row['Open'] - row['Close'] >= 0 else '#2B8308' for index, row in self.data.iterrows()]
26
+ self.fig.add_trace(go.Bar(x=self.data.index, y=self.data['Volume'], showlegend=False, marker_color=colors), row=2, col=1)
27
+
28
+ def render_chart(self):
29
+ self.fig.update_layout(title='Historical Price and Volume', height=500, margin=dict(l=0, r=10, b=10, t=25))
30
+ st.plotly_chart(self.fig, use_container_width=True)
31
+
32
+ class Plots:
33
+ def __init__(self, data):
34
+ self.data = data
35
+
36
+ def plot_predictions(self, predictions, future_predictions):
37
+
38
+ predicted_dates = self.data.index[-len(predictions):]
39
+ future_dates = pd.date_range(start=self.data.index[-1] + pd.Timedelta(days=1), periods=len(future_predictions), freq='B')
40
+ predictions = [float(val) for val in predictions if pd.notna(val)]
41
+ future_predictions = [float(val) for val in future_predictions if pd.notna(val)]
42
+
43
+ fig = make_subplots(rows=1, cols=1)
44
+ fig.add_trace(go.Scatter(x=self.data.index, y=self.data['Close'], mode='lines', name='Actual Stock Prices', marker_color='blue'))
45
+ fig.add_trace(go.Scatter(x=predicted_dates, y=predictions, mode='lines', name='LSTM Predicted Prices', marker_color='red', line=dict(dash='dash')))
46
+ fig.add_trace(go.Scatter(x=future_dates, y=future_predictions, mode='lines', name='Future Predictions', marker_color='green', line=dict(dash='dot')))
47
+
48
+ fig.update_layout(title='Comparison of Actual, Predicted, and Future Stock Prices', xaxis_title='Date', yaxis_title='Price', legend_title='Legend', height=500)
49
+ st.plotly_chart(fig, use_container_width=True)
STOCK-MARKET-APP/requirement.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ numpy
2
+ pandas
3
+ seaborn
4
+ matplotlib
5
+ keras
6
+ tensorflow
7
+ scikit-learn
8
+ yfinance
9
+ plotly
STOCK-MARKET-APP/stock_data_loader.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import yfinance as yf
3
+
4
+ import warnings
5
+ warnings.filterwarnings("ignore")
6
+
7
+ class StockDataLoader:
8
+ def __init__(self, ticker, start_date, end_date):
9
+ self.ticker = ticker
10
+ self.start_date = start_date
11
+ self.end_date = end_date
12
+
13
+ def get_stock_data(self):
14
+ stock = yf.Ticker(self.ticker)
15
+ stock_data = stock.history(start=self.start_date, end=self.end_date)
16
+ stock_data.reset_index(inplace=True)
17
+ stock_data['Date'] = pd.to_datetime(stock_data['Date'])
18
+ stock_data.set_index('Date', inplace=True)
19
+ return stock_data
STOCK-MARKET-APP/utils.py ADDED
@@ -0,0 +1,324 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ import streamlit as st
3
+ import yfinance as yf
4
+ from functools import wraps
5
+ import pandas as pd
6
+ import numpy as np
7
+ import random
8
+ from datetime import datetime, timedelta
9
+ try:
10
+ import pandas_datareader.data as web
11
+ PANDAS_DATAREADER_AVAILABLE = True
12
+ except ImportError:
13
+ PANDAS_DATAREADER_AVAILABLE = False
14
+ st.warning("pandas_datareader not available. Install it with: pip install pandas-datareader")
15
+
16
+ class RateLimitManager:
17
+ """Manages rate limiting for API calls"""
18
+
19
+ def __init__(self, min_delay=3.0):
20
+ self.min_delay = min_delay
21
+ self.last_call_time = 0
22
+
23
+ def wait_if_needed(self):
24
+ """Ensure minimum delay between API calls"""
25
+ current_time = time.time()
26
+ time_since_last_call = current_time - self.last_call_time
27
+
28
+ if time_since_last_call < self.min_delay:
29
+ sleep_time = self.min_delay - time_since_last_call + random.uniform(0.5, 1.5)
30
+ time.sleep(sleep_time)
31
+
32
+ self.last_call_time = time.time()
33
+
34
+ # Global rate limit manager
35
+ rate_limiter = RateLimitManager()
36
+
37
+ def create_sample_data(ticker, period='1mo'):
38
+ """Create sample data when API is unavailable"""
39
+
40
+ # Define sample data for common tickers
41
+ sample_data = {
42
+ 'NVDA': {'base_price': 450, 'volatility': 0.03, 'trend': 0.001},
43
+ 'AAPL': {'base_price': 190, 'volatility': 0.02, 'trend': 0.0005},
44
+ 'GOOGL': {'base_price': 140, 'volatility': 0.025, 'trend': 0.0008},
45
+ 'MSFT': {'base_price': 420, 'volatility': 0.02, 'trend': 0.0007},
46
+ 'AMZN': {'base_price': 150, 'volatility': 0.025, 'trend': 0.0006}
47
+ }
48
+
49
+ # Get parameters for ticker or use defaults
50
+ params = sample_data.get(ticker, {'base_price': 100, 'volatility': 0.02, 'trend': 0.0005})
51
+
52
+ # Generate date range based on period
53
+ if period == 'max' or period == '1y':
54
+ days = 252
55
+ elif period == '6mo':
56
+ days = 126
57
+ elif period == '1mo':
58
+ days = 30
59
+ else:
60
+ days = 30
61
+
62
+ # Create date range
63
+ end_date = datetime.now()
64
+ start_date = end_date - timedelta(days=days)
65
+ dates = pd.date_range(start=start_date, end=end_date, freq='D')
66
+
67
+ # Remove weekends
68
+ dates = dates[dates.weekday < 5]
69
+
70
+ # Generate price data
71
+ np.random.seed(42) # For consistent sample data
72
+ returns = np.random.normal(params['trend'], params['volatility'], len(dates))
73
+
74
+ prices = [params['base_price']]
75
+ for ret in returns[1:]:
76
+ prices.append(prices[-1] * (1 + ret))
77
+
78
+ # Create DataFrame
79
+ df = pd.DataFrame(index=dates[:len(prices)])
80
+ df['Close'] = prices
81
+ df['Open'] = df['Close'].shift(1).fillna(df['Close'])
82
+ df['High'] = df['Close'] * (1 + np.random.uniform(0, 0.02, len(df)))
83
+ df['Low'] = df['Close'] * (1 - np.random.uniform(0, 0.02, len(df)))
84
+ df['Volume'] = np.random.randint(1000000, 10000000, len(df))
85
+
86
+ return df
87
+
88
+ def retry_with_backoff(max_retries=5, base_delay=10):
89
+ """Decorator for retrying functions with exponential backoff"""
90
+ def decorator(func):
91
+ @wraps(func)
92
+ def wrapper(*args, **kwargs):
93
+ for attempt in range(max_retries):
94
+ try:
95
+ rate_limiter.wait_if_needed()
96
+ return func(*args, **kwargs)
97
+ except Exception as e:
98
+ error_msg = str(e).lower()
99
+
100
+ if any(keyword in error_msg for keyword in ['rate', 'limit', '429', 'too many requests']):
101
+ if attempt < max_retries - 1:
102
+ wait_time = base_delay * (2 ** attempt) + random.uniform(2, 5)
103
+ st.warning(f"🚫 Rate limit hit. Waiting {wait_time:.1f} seconds before retry {attempt + 2}/{max_retries}...")
104
+ time.sleep(wait_time)
105
+ continue
106
+ else:
107
+ st.error("⏱️ Rate limit exceeded after all retries. Using sample data.")
108
+ return None
109
+ elif any(keyword in error_msg for keyword in ['expecting value', 'no timezone', 'delisted', 'json']):
110
+ if attempt < max_retries - 1:
111
+ wait_time = base_delay + random.uniform(2, 4)
112
+ st.warning(f"🔄 Data parsing error. Retrying in {wait_time:.1f} seconds... (attempt {attempt + 2}/{max_retries})")
113
+ time.sleep(wait_time)
114
+ continue
115
+ else:
116
+ st.warning("⚠️ Unable to fetch real data. Using sample data for demonstration.")
117
+ return None
118
+ else:
119
+ if attempt < max_retries - 1:
120
+ wait_time = base_delay + random.uniform(1, 3)
121
+ st.warning(f"❗ Error: {str(e)[:100]}... Retrying in {wait_time:.1f} seconds...")
122
+ time.sleep(wait_time)
123
+ continue
124
+ else:
125
+ st.error(f"❌ Failed after {max_retries} attempts: {str(e)[:100]}...")
126
+ return None
127
+ return None
128
+ return wrapper
129
+ return decorator
130
+
131
+ def fetch_data_with_stooq(ticker_symbol, start_date=None, end_date=None, period='1mo'):
132
+ """Fetch stock data using pandas_datareader with stooq as source"""
133
+ if not PANDAS_DATAREADER_AVAILABLE:
134
+ return None
135
+
136
+ try:
137
+ # Convert period to date range if start/end not provided
138
+ if start_date is None or end_date is None:
139
+ end_date = datetime.now()
140
+ if period == 'max' or period == '1y':
141
+ start_date = end_date - timedelta(days=365)
142
+ elif period == '6mo':
143
+ start_date = end_date - timedelta(days=180)
144
+ elif period == '1mo':
145
+ start_date = end_date - timedelta(days=30)
146
+ elif period == '5d':
147
+ start_date = end_date - timedelta(days=5)
148
+ else:
149
+ start_date = end_date - timedelta(days=30)
150
+
151
+ # Fetch data from stooq
152
+ df = web.DataReader(ticker_symbol, 'stooq', start_date, end_date)
153
+
154
+ if df.empty:
155
+ return None
156
+
157
+ # Stooq returns data in reverse chronological order, so sort it
158
+ df = df.sort_index()
159
+
160
+ # Ensure we have the required columns
161
+ required_columns = ['Open', 'High', 'Low', 'Close', 'Volume']
162
+ if all(col in df.columns for col in required_columns):
163
+ return df
164
+ else:
165
+ st.warning(f"Missing columns in stooq data: {[col for col in required_columns if col not in df.columns]}")
166
+ return None
167
+
168
+ except Exception as e:
169
+ st.error(f"Error fetching data from stooq: {str(e)}")
170
+ return None
171
+
172
+ def safe_yfinance_call(ticker_symbol, operation='history', **kwargs):
173
+ """Safely call multiple data sources with fallback to sample data"""
174
+
175
+ # First try stooq (pandas_datareader) for historical data
176
+ if operation == 'history' and PANDAS_DATAREADER_AVAILABLE:
177
+ try:
178
+ st.sidebar.info(f"🔄 Trying stooq API for {ticker_symbol}...")
179
+ stooq_data = fetch_data_with_stooq(
180
+ ticker_symbol,
181
+ start_date=kwargs.get('start'),
182
+ end_date=kwargs.get('end'),
183
+ period=kwargs.get('period', '1mo')
184
+ )
185
+
186
+ if stooq_data is not None and not stooq_data.empty:
187
+ st.sidebar.success(f"✅ Real data from stooq for {ticker_symbol}")
188
+ return stooq_data
189
+ else:
190
+ st.sidebar.warning(f"⚠️ Stooq failed for {ticker_symbol}")
191
+ except Exception as e:
192
+ st.sidebar.warning(f"⚠️ Stooq error: {str(e)[:50]}...")
193
+
194
+ # If stooq fails or for info operation, try yfinance as backup
195
+ try:
196
+ st.sidebar.info(f"🔄 Trying yfinance API for {ticker_symbol}...")
197
+ ticker = yf.Ticker(ticker_symbol)
198
+
199
+ if operation == 'history':
200
+ result = ticker.history(
201
+ timeout=10,
202
+ prepost=False,
203
+ auto_adjust=True,
204
+ back_adjust=False,
205
+ repair=True,
206
+ keepna=False,
207
+ actions=False,
208
+ **kwargs
209
+ )
210
+
211
+ if result is not None and not result.empty and len(result) > 0:
212
+ st.sidebar.success(f"✅ Real data from yfinance for {ticker_symbol}")
213
+ return result
214
+ else:
215
+ st.sidebar.warning(f"⚠️ yfinance returned empty data for {ticker_symbol}")
216
+
217
+ elif operation == 'info':
218
+ result = ticker.info
219
+ if result and isinstance(result, dict) and len(result) > 1:
220
+ st.sidebar.success(f"✅ Info from yfinance for {ticker_symbol}")
221
+ return result
222
+ else:
223
+ st.sidebar.warning(f"⚠️ yfinance info empty for {ticker_symbol}")
224
+
225
+ else:
226
+ raise ValueError(f"Unsupported operation: {operation}")
227
+
228
+ except Exception as e:
229
+ st.sidebar.warning(f"⚠️ yfinance also failed: {str(e)[:50]}...")
230
+
231
+ # Finally fallback to sample data
232
+ if operation == 'history':
233
+ st.sidebar.warning(f"📊 Using sample data for {ticker_symbol}")
234
+ return create_sample_data(ticker_symbol, kwargs.get('period', '1mo'))
235
+ elif operation == 'info':
236
+ sample_prices = {
237
+ 'NVDA': 450, 'AAPL': 190, 'GOOGL': 140, 'MSFT': 420, 'AMZN': 150
238
+ }
239
+ base_price = sample_prices.get(ticker_symbol, 100)
240
+ return {
241
+ 'symbol': ticker_symbol,
242
+ 'shortName': f'{ticker_symbol} Inc.',
243
+ 'currentPrice': base_price + random.uniform(-2, 2),
244
+ 'previousClose': base_price
245
+ }
246
+ else:
247
+ raise Exception(f"All data sources failed for {ticker_symbol}")
248
+
249
+ def get_cached_data(cache_key, ttl_seconds=300):
250
+ """Get cached data from session state if still valid"""
251
+ if cache_key in st.session_state:
252
+ cache_time_key = f"cache_time_{cache_key}"
253
+ if cache_time_key in st.session_state:
254
+ cache_time = st.session_state[cache_time_key]
255
+ if time.time() - cache_time < ttl_seconds:
256
+ return st.session_state[cache_key]
257
+ return None
258
+
259
+ def set_cached_data(cache_key, data):
260
+ """Cache data in session state with timestamp"""
261
+ st.session_state[cache_key] = data
262
+ st.session_state[f"cache_time_{cache_key}"] = time.time()
263
+
264
+ def clear_cache(pattern=None):
265
+ """Clear cached data matching pattern"""
266
+ if pattern is None:
267
+ # Clear all cache
268
+ keys_to_remove = [key for key in st.session_state.keys()
269
+ if key.startswith('cache_time_') or key.startswith('data_')]
270
+ else:
271
+ keys_to_remove = [key for key in st.session_state.keys() if pattern in key]
272
+
273
+ for key in keys_to_remove:
274
+ del st.session_state[key]
275
+
276
+ return len(keys_to_remove)
277
+
278
+ def format_error_message(error):
279
+ """Format error messages for better user experience"""
280
+ error_str = str(error).lower()
281
+
282
+ if "rate" in error_str or "limit" in error_str:
283
+ return ("🚫 **Rate Limit Exceeded**\n\n"
284
+ "Yahoo Finance has temporarily limited your requests. This happens when too many requests are made in a short time.\n\n"
285
+ "**What you can do:**\n"
286
+ "- Wait 5-10 minutes before trying again\n"
287
+ "- Use the cached data if available\n"
288
+ "- Try a different stock ticker\n\n"
289
+ "The app will automatically retry with delays between requests.")
290
+ elif "network" in error_str or "connection" in error_str:
291
+ return ("🌐 **Network Error**\n\n"
292
+ "There seems to be a connectivity issue.\n\n"
293
+ "**What you can do:**\n"
294
+ "- Check your internet connection\n"
295
+ "- Try refreshing the page\n"
296
+ "- Wait a moment and try again")
297
+ else:
298
+ return f"❌ **Error**: {str(error)}"
299
+
300
+ def display_cache_info():
301
+ """Display cache information in sidebar"""
302
+ with st.sidebar:
303
+ with st.expander("Cache Information"):
304
+ cache_items = [key for key in st.session_state.keys()
305
+ if key.startswith('data_') or key.startswith('model_data_')]
306
+
307
+ if cache_items:
308
+ st.write(f"**Cached items:** {len(cache_items)}")
309
+ for item in cache_items[:5]: # Show first 5 items
310
+ cache_time_key = f"cache_time_{item}"
311
+ if cache_time_key in st.session_state:
312
+ cache_time = st.session_state[cache_time_key]
313
+ age_minutes = (time.time() - cache_time) / 60
314
+ st.write(f"• {item.replace('data_', '')}: {age_minutes:.1f}m ago")
315
+
316
+ if len(cache_items) > 5:
317
+ st.write(f"... and {len(cache_items) - 5} more")
318
+
319
+ if st.button("Clear All Cache"):
320
+ cleared = clear_cache()
321
+ st.success(f"Cleared {cleared} cached items")
322
+ st.experimental_rerun()
323
+ else:
324
+ st.write("No cached data")
STOCK-MARKET-APP/view_page.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from stock_data_loader import StockDataLoader
2
+ import streamlit as st
3
+ import pandas as pd
4
+ import yfinance as yf
5
+ from datetime import datetime
6
+ from plots import Plots, StockChart
7
+
8
+ class StockDashboard:
9
+ def __init__(self):
10
+ self.tickers = ['NVDA', 'AAPL', 'GOOGL', 'MSFT', 'AMZN']
11
+ self.period_map = {'all': 'max','1m': '1mo', '6m': '6mo', '1y': '1y'}
12
+
13
+ def render_sidebar(self):
14
+ st.sidebar.header("Choose your filter:")
15
+ self.ticker = st.sidebar.selectbox('Choose Ticker', options=self.tickers, help='Select a ticker')
16
+ self.selected_range = st.sidebar.selectbox('Select Period', options=list(self.period_map.keys()))
17
+
18
+ def load_data(self):
19
+ self.yf_data = yf.Ticker(self.ticker)
20
+ self.df_history = self.yf_data.history(period=self.period_map[self.selected_range])
21
+ self.current_price = self.yf_data.info.get('currentPrice', 'N/A')
22
+ self.previous_close = self.yf_data.info.get('previousClose', 'N/A')
23
+
24
+ def display_header(self):
25
+ company_name = self.yf_data.info['shortName']
26
+ symbol = self.yf_data.info['symbol']
27
+ st.subheader(f'{company_name} ({symbol}) 💰')
28
+ st.divider()
29
+ if self.current_price != 'N/A' and self.previous_close != 'N/A':
30
+ price_change = self.current_price - self.previous_close
31
+ price_change_ratio = (abs(price_change) / self.previous_close * 100)
32
+ price_change_direction = "+" if price_change > 0 else "-"
33
+ st.metric(label='Current Price', value=f"{self.current_price:.2f}",
34
+ delta=f"{price_change:.2f} ({price_change_direction}{price_change_ratio:.2f}%)")
35
+
36
+ def plot_data(self):
37
+ chart = StockChart(self.df_history)
38
+ chart.add_price_chart()
39
+ chart.add_oversold_overbought_lines()
40
+ chart.add_volume_chart()
41
+ chart.render_chart()
42
+
43
+ def run(self):
44
+ st.write("--------------------------------------------")
45
+ self.render_sidebar()
46
+ self.load_data()
47
+ self.display_header()
48
+ self.plot_data()