Shaikat01 commited on
Commit
fe4ed55
ยท
verified ยท
1 Parent(s): 7802534

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +406 -325
app.py CHANGED
@@ -1,325 +1,406 @@
1
- import gradio as gr
2
- import pandas as pd
3
- import numpy as np
4
- import plotly.graph_objects as go
5
- from datetime import datetime, timedelta
6
- import pickle
7
- import yfinance as yf
8
- from statsmodels.tsa.arima.model import ARIMA
9
- from prophet import Prophet
10
- from tensorflow import keras
11
- from sklearn.preprocessing import MinMaxScaler
12
- import warnings
13
- warnings.filterwarnings('ignore')
14
-
15
- # Load your saved models (update paths as needed)
16
- # For Hugging Face, these will be in the same directory as app.py
17
- def load_models():
18
- """Load all three models"""
19
- try:
20
- # Load ARIMA model
21
- with open('arima_model.pkl', 'rb') as f:
22
- arima_model = pickle.load(f)
23
-
24
- # Load Prophet model
25
- with open('prophet_model.pkl', 'rb') as f:
26
- prophet_model = pickle.load(f)
27
-
28
- # Load LSTM model and scaler
29
- lstm_model = keras.models.load_model('lstm_model.h5')
30
- with open('lstm_scaler.pkl', 'rb') as f:
31
- scaler = pickle.load(f)
32
-
33
- return arima_model, prophet_model, lstm_model, scaler
34
- except Exception as e:
35
- print(f"Error loading models: {e}")
36
- return None, None, None, None
37
-
38
- # Global variables for models
39
- arima_model, prophet_model, lstm_model, scaler = load_models()
40
- SEQ_LENGTH = 60 # Should match your training
41
-
42
- def fetch_stock_data(ticker, days=365):
43
- """Fetch stock data from Yahoo Finance"""
44
- try:
45
- end_date = datetime.now()
46
- start_date = end_date - timedelta(days=days)
47
- df = yf.download(ticker, start=start_date, end=end_date, progress=False)
48
- if df.empty:
49
- return None, f"No data found for ticker: {ticker}"
50
- df = df[['Close']].copy()
51
- df.columns = ['Price']
52
- return df, None
53
- except Exception as e:
54
- return None, str(e)
55
-
56
- def make_arima_forecast(data, days):
57
- """Make ARIMA forecast"""
58
- try:
59
- # Retrain ARIMA with recent data (or use loaded model)
60
- model = ARIMA(data['Price'], order=(1, 1, 1))
61
- fitted = model.fit()
62
- forecast = fitted.forecast(steps=days)
63
- return forecast.values
64
- except Exception as e:
65
- print(f"ARIMA Error: {e}")
66
- return None
67
-
68
- def make_prophet_forecast(data, days):
69
- """Make Prophet forecast"""
70
- try:
71
- # Prepare data for Prophet
72
- prophet_data = pd.DataFrame({
73
- 'ds': data.index,
74
- 'y': data['Price'].values
75
- })
76
-
77
- # Create and fit model
78
- model = Prophet(
79
- daily_seasonality=True,
80
- weekly_seasonality=True,
81
- yearly_seasonality=True,
82
- changepoint_prior_scale=0.05
83
- )
84
- model.fit(prophet_data)
85
-
86
- # Make forecast
87
- future = model.make_future_dataframe(periods=days)
88
- forecast = model.predict(future)
89
- return forecast['yhat'].tail(days).values
90
- except Exception as e:
91
- print(f"Prophet Error: {e}")
92
- return None
93
-
94
- def make_lstm_forecast(data, days, model, scaler, seq_length=60):
95
- """Make LSTM forecast"""
96
- try:
97
- # Scale the data
98
- scaled_data = scaler.transform(data[['Price']])
99
-
100
- # Prepare the last sequence
101
- last_sequence = scaled_data[-seq_length:].reshape(1, seq_length, 1)
102
-
103
- predictions = []
104
- current_sequence = last_sequence.copy()
105
-
106
- # Generate predictions day by day
107
- for _ in range(days):
108
- pred = model.predict(current_sequence, verbose=0)
109
- predictions.append(pred[0, 0])
110
-
111
- # Update sequence
112
- current_sequence = np.append(current_sequence[:, 1:, :],
113
- pred.reshape(1, 1, 1), axis=1)
114
-
115
- # Inverse transform predictions
116
- predictions = scaler.inverse_transform(np.array(predictions).reshape(-1, 1))
117
- return predictions.flatten()
118
- except Exception as e:
119
- print(f"LSTM Error: {e}")
120
- return None
121
-
122
- def create_forecast_plot(historical_data, forecasts, ticker, model_names):
123
- """Create interactive plotly chart"""
124
- fig = go.Figure()
125
-
126
- # Historical data
127
- fig.add_trace(go.Scatter(
128
- x=historical_data.index,
129
- y=historical_data['Price'],
130
- mode='lines',
131
- name='Historical Price',
132
- line=dict(color='blue', width=2)
133
- ))
134
-
135
- # Generate future dates
136
- last_date = historical_data.index[-1]
137
- future_dates = pd.date_range(start=last_date + timedelta(days=1),
138
- periods=len(forecasts[0]))
139
-
140
- # Plot forecasts
141
- colors = ['red', 'purple', 'orange']
142
- for i, (forecast, name) in enumerate(zip(forecasts, model_names)):
143
- if forecast is not None:
144
- fig.add_trace(go.Scatter(
145
- x=future_dates,
146
- y=forecast,
147
- mode='lines+markers',
148
- name=f'{name} Forecast',
149
- line=dict(color=colors[i], width=2, dash='dash'),
150
- marker=dict(size=6)
151
- ))
152
-
153
- fig.update_layout(
154
- title=f'{ticker} Stock Price Forecast',
155
- xaxis_title='Date',
156
- yaxis_title='Price ($)',
157
- hovermode='x unified',
158
- template='plotly_white',
159
- height=600,
160
- showlegend=True,
161
- legend=dict(
162
- yanchor="top",
163
- y=0.99,
164
- xanchor="left",
165
- x=0.01
166
- )
167
- )
168
-
169
- return fig
170
-
171
- def predict_stock(ticker, forecast_days, model_choice):
172
- """Main prediction function"""
173
- # Validate inputs
174
- if not ticker:
175
- return None, "Please enter a stock ticker symbol", None
176
-
177
- ticker = ticker.upper().strip()
178
-
179
- # Fetch data
180
- data, error = fetch_stock_data(ticker, days=730) # 2 years of data
181
- if error:
182
- return None, f"Error: {error}", None
183
-
184
- # Make forecasts based on model choice
185
- forecasts = []
186
- model_names = []
187
-
188
- if model_choice in ["All Models", "ARIMA"]:
189
- arima_forecast = make_arima_forecast(data, forecast_days)
190
- if arima_forecast is not None:
191
- forecasts.append(arima_forecast)
192
- model_names.append("ARIMA")
193
-
194
- if model_choice in ["All Models", "Prophet"]:
195
- prophet_forecast = make_prophet_forecast(data, forecast_days)
196
- if prophet_forecast is not None:
197
- forecasts.append(prophet_forecast)
198
- model_names.append("Prophet")
199
-
200
- if model_choice in ["All Models", "LSTM"] and lstm_model is not None:
201
- lstm_forecast = make_lstm_forecast(data, forecast_days, lstm_model, scaler, SEQ_LENGTH)
202
- if lstm_forecast is not None:
203
- forecasts.append(lstm_forecast)
204
- model_names.append("LSTM")
205
-
206
- if not forecasts:
207
- return None, "Failed to generate forecasts. Please try again.", None
208
-
209
- # Create plot
210
- fig = create_forecast_plot(data, forecasts, ticker, model_names)
211
-
212
- # Create forecast table
213
- future_dates = pd.date_range(
214
- start=data.index[-1] + timedelta(days=1),
215
- periods=forecast_days
216
- )
217
-
218
- forecast_df = pd.DataFrame({'Date': future_dates.strftime('%Y-%m-%d')})
219
- for forecast, name in zip(forecasts, model_names):
220
- forecast_df[f'{name} Prediction ($)'] = np.round(forecast, 2)
221
-
222
- # Summary statistics
223
- summary = f"""
224
- ๐Ÿ“Š **Forecast Summary for {ticker}**
225
-
226
- - Current Price: ${data['Price'].iloc[-1]:.2f}
227
- - Forecast Period: {forecast_days} days
228
- - Models Used: {', '.join(model_names)}
229
-
230
- **Predicted Price Range (Day {forecast_days}):**
231
- """
232
-
233
- for forecast, name in zip(forecasts, model_names):
234
- final_price = forecast[-1]
235
- change = ((final_price - data['Price'].iloc[-1]) / data['Price'].iloc[-1]) * 100
236
- summary += f"\n- {name}: ${final_price:.2f} ({change:+.2f}%)"
237
-
238
- return fig, summary, forecast_df
239
-
240
- # Create Gradio Interface
241
- with gr.Blocks(title="Stock Price Forecasting", theme=gr.themes.Soft()) as demo:
242
- gr.Markdown(
243
- """
244
- # ๐Ÿ“ˆ Stock Price Forecasting App
245
-
246
- Predict future stock prices using ARIMA, Prophet, and LSTM models.
247
- Enter a stock ticker symbol and select forecast parameters below.
248
-
249
- **Note:** Predictions are for educational purposes only. Not financial advice.
250
- """
251
- )
252
-
253
- with gr.Row():
254
- with gr.Column(scale=1):
255
- ticker_input = gr.Textbox(
256
- label="Stock Ticker Symbol",
257
- placeholder="e.g., AAPL, GOOGL, TSLA",
258
- value="AAPL"
259
- )
260
-
261
- forecast_days = gr.Slider(
262
- minimum=1,
263
- maximum=90,
264
- value=30,
265
- step=1,
266
- label="Forecast Days"
267
- )
268
-
269
- model_choice = gr.Radio(
270
- choices=["All Models", "ARIMA", "Prophet", "LSTM"],
271
- value="All Models",
272
- label="Select Model(s)"
273
- )
274
-
275
- predict_btn = gr.Button("๐Ÿ”ฎ Generate Forecast", variant="primary", size="lg")
276
-
277
- with gr.Column(scale=2):
278
- output_plot = gr.Plot(label="Forecast Visualization")
279
-
280
- with gr.Row():
281
- output_summary = gr.Markdown(label="Forecast Summary")
282
-
283
- with gr.Row():
284
- output_table = gr.Dataframe(
285
- label="Detailed Forecast",
286
- wrap=True,
287
- interactive=False
288
- )
289
-
290
- # Examples
291
- gr.Examples(
292
- examples=[
293
- ["AAPL", 30, "All Models"],
294
- ["GOOGL", 14, "Prophet"],
295
- ["TSLA", 60, "LSTM"],
296
- ["MSFT", 45, "ARIMA"],
297
- ],
298
- inputs=[ticker_input, forecast_days, model_choice],
299
- )
300
-
301
- # Connect the button to the function
302
- predict_btn.click(
303
- fn=predict_stock,
304
- inputs=[ticker_input, forecast_days, model_choice],
305
- outputs=[output_plot, output_summary, output_table]
306
- )
307
-
308
- gr.Markdown(
309
- """
310
- ---
311
- ### ๐Ÿ“š About the Models
312
-
313
- - **ARIMA**: Statistical model for time series forecasting
314
- - **Prophet**: Facebook's forecasting tool, excellent for seasonality
315
- - **LSTM**: Deep learning model that captures complex patterns
316
-
317
- ### โš ๏ธ Disclaimer
318
- This tool is for educational and research purposes only. Stock market predictions are inherently uncertain.
319
- Always conduct thorough research and consult with financial advisors before making investment decisions.
320
- """
321
- )
322
-
323
- # Launch the app
324
- if __name__ == "__main__":
325
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+ import numpy as np
4
+ import plotly.graph_objects as go
5
+ from datetime import datetime, timedelta
6
+ import yfinance as yf
7
+ from statsmodels.tsa.arima.model import ARIMA
8
+ from prophet import Prophet
9
+ import warnings
10
+ warnings.filterwarnings('ignore')
11
+
12
+ # NO PRE-TRAINED MODELS - Train on demand with user's data
13
+ # This avoids the 50GB storage limit issue
14
+
15
+ def fetch_stock_data(ticker, days=730):
16
+ """Fetch stock data from Yahoo Finance"""
17
+ try:
18
+ end_date = datetime.now()
19
+ start_date = end_date - timedelta(days=days)
20
+ df = yf.download(ticker, start=start_date, end=end_date, progress=False)
21
+ if df.empty:
22
+ return None, f"No data found for ticker: {ticker}"
23
+ df = df[['Close']].copy()
24
+ df.columns = ['Price']
25
+ df = df.dropna()
26
+ return df, None
27
+ except Exception as e:
28
+ return None, str(e)
29
+
30
+ def make_arima_forecast(data, days):
31
+ """Train ARIMA and make forecast"""
32
+ try:
33
+ # Train ARIMA model on-the-fly
34
+ model = ARIMA(data['Price'], order=(1, 1, 1))
35
+ fitted = model.fit()
36
+ forecast = fitted.forecast(steps=days)
37
+ return forecast.values
38
+ except Exception as e:
39
+ print(f"ARIMA Error: {e}")
40
+ return None
41
+
42
+ def make_prophet_forecast(data, days):
43
+ """Train Prophet and make forecast"""
44
+ try:
45
+ # Prepare data for Prophet
46
+ prophet_data = pd.DataFrame({
47
+ 'ds': data.index,
48
+ 'y': data['Price'].values
49
+ })
50
+
51
+ # Create and train model on-the-fly
52
+ model = Prophet(
53
+ daily_seasonality=False,
54
+ weekly_seasonality=True,
55
+ yearly_seasonality=True,
56
+ changepoint_prior_scale=0.05,
57
+ seasonality_mode='multiplicative'
58
+ )
59
+ model.fit(prophet_data)
60
+
61
+ # Make forecast
62
+ future = model.make_future_dataframe(periods=days)
63
+ forecast = model.predict(future)
64
+ return forecast['yhat'].tail(days).values
65
+ except Exception as e:
66
+ print(f"Prophet Error: {e}")
67
+ return None
68
+
69
+ def make_simple_ml_forecast(data, days):
70
+ """Simple exponential smoothing forecast (lightweight alternative to LSTM)"""
71
+ try:
72
+ from statsmodels.tsa.holtwinters import ExponentialSmoothing
73
+
74
+ # Train exponential smoothing model
75
+ model = ExponentialSmoothing(
76
+ data['Price'],
77
+ seasonal_periods=30,
78
+ trend='add',
79
+ seasonal='add'
80
+ )
81
+ fitted = model.fit()
82
+ forecast = fitted.forecast(steps=days)
83
+ return forecast.values
84
+ except Exception as e:
85
+ print(f"ML Forecast Error: {e}")
86
+ return None
87
+
88
+ def calculate_moving_average_forecast(data, days, window=20):
89
+ """Simple moving average forecast"""
90
+ try:
91
+ ma = data['Price'].rolling(window=window).mean().iloc[-1]
92
+ trend = (data['Price'].iloc[-1] - data['Price'].iloc[-window]) / window
93
+ forecast = [ma + trend * i for i in range(1, days + 1)]
94
+ return np.array(forecast)
95
+ except Exception as e:
96
+ print(f"MA Error: {e}")
97
+ return None
98
+
99
+ def create_forecast_plot(historical_data, forecasts, ticker, model_names):
100
+ """Create interactive plotly chart"""
101
+ fig = go.Figure()
102
+
103
+ # Show last 90 days of historical data for clarity
104
+ recent_data = historical_data.tail(90)
105
+
106
+ # Historical data
107
+ fig.add_trace(go.Scatter(
108
+ x=recent_data.index,
109
+ y=recent_data['Price'],
110
+ mode='lines',
111
+ name='Historical Price',
112
+ line=dict(color='blue', width=2)
113
+ ))
114
+
115
+ # Generate future dates
116
+ last_date = historical_data.index[-1]
117
+ future_dates = pd.date_range(start=last_date + timedelta(days=1),
118
+ periods=len(forecasts[0]))
119
+
120
+ # Plot forecasts
121
+ colors = ['red', 'purple', 'orange', 'green']
122
+ for i, (forecast, name) in enumerate(zip(forecasts, model_names)):
123
+ if forecast is not None:
124
+ fig.add_trace(go.Scatter(
125
+ x=future_dates,
126
+ y=forecast,
127
+ mode='lines+markers',
128
+ name=f'{name} Forecast',
129
+ line=dict(color=colors[i], width=2, dash='dash'),
130
+ marker=dict(size=4)
131
+ ))
132
+
133
+ # Add vertical line at prediction start
134
+ fig.add_vline(
135
+ x=last_date,
136
+ line_dash="dash",
137
+ line_color="gray",
138
+ annotation_text="Forecast Start"
139
+ )
140
+
141
+ fig.update_layout(
142
+ title=f'{ticker} Stock Price Forecast',
143
+ xaxis_title='Date',
144
+ yaxis_title='Price ($)',
145
+ hovermode='x unified',
146
+ template='plotly_white',
147
+ height=600,
148
+ showlegend=True,
149
+ legend=dict(
150
+ yanchor="top",
151
+ y=0.99,
152
+ xanchor="left",
153
+ x=0.01,
154
+ bgcolor="rgba(255, 255, 255, 0.8)"
155
+ )
156
+ )
157
+
158
+ return fig
159
+
160
+ def predict_stock(ticker, forecast_days, model_choice):
161
+ """Main prediction function"""
162
+ # Validate inputs
163
+ if not ticker:
164
+ return None, "โŒ Please enter a stock ticker symbol", None
165
+
166
+ ticker = ticker.upper().strip()
167
+
168
+ # Show loading message
169
+ status_msg = f"๐Ÿ”„ Fetching data for {ticker}..."
170
+
171
+ # Fetch data (2 years for better training)
172
+ data, error = fetch_stock_data(ticker, days=730)
173
+ if error:
174
+ return None, f"โŒ Error: {error}", None
175
+
176
+ if len(data) < 60:
177
+ return None, f"โŒ Insufficient data for {ticker}. Need at least 60 days of history.", None
178
+
179
+ status_msg += f"\nโœ… Found {len(data)} days of data\n๐Ÿ”„ Training models..."
180
+
181
+ # Make forecasts based on model choice
182
+ forecasts = []
183
+ model_names = []
184
+
185
+ if model_choice in ["All Models", "ARIMA"]:
186
+ arima_forecast = make_arima_forecast(data, forecast_days)
187
+ if arima_forecast is not None:
188
+ forecasts.append(arima_forecast)
189
+ model_names.append("ARIMA")
190
+
191
+ if model_choice in ["All Models", "Prophet"]:
192
+ prophet_forecast = make_prophet_forecast(data, forecast_days)
193
+ if prophet_forecast is not None:
194
+ forecasts.append(prophet_forecast)
195
+ model_names.append("Prophet")
196
+
197
+ if model_choice in ["All Models", "Exp. Smoothing"]:
198
+ ml_forecast = make_simple_ml_forecast(data, forecast_days)
199
+ if ml_forecast is not None:
200
+ forecasts.append(ml_forecast)
201
+ model_names.append("Exp. Smoothing")
202
+
203
+ if model_choice in ["All Models", "Moving Average"]:
204
+ ma_forecast = calculate_moving_average_forecast(data, forecast_days)
205
+ if ma_forecast is not None:
206
+ forecasts.append(ma_forecast)
207
+ model_names.append("Moving Average")
208
+
209
+ if not forecasts:
210
+ return None, "โŒ Failed to generate forecasts. Please try again.", None
211
+
212
+ # Create plot
213
+ fig = create_forecast_plot(data, forecasts, ticker, model_names)
214
+
215
+ # Create forecast table
216
+ future_dates = pd.date_range(
217
+ start=data.index[-1] + timedelta(days=1),
218
+ periods=forecast_days
219
+ )
220
+
221
+ forecast_df = pd.DataFrame({'Date': future_dates.strftime('%Y-%m-%d')})
222
+ for forecast, name in zip(forecasts, model_names):
223
+ forecast_df[f'{name} ($)'] = np.round(forecast, 2)
224
+
225
+ # Calculate statistics
226
+ current_price = data['Price'].iloc[-1]
227
+ avg_forecast = np.mean([f[-1] for f in forecasts])
228
+ avg_change = ((avg_forecast - current_price) / current_price) * 100
229
+
230
+ # Summary statistics
231
+ summary = f"""
232
+ ## ๐Ÿ“Š Forecast Summary for **{ticker}**
233
+
234
+ ### Current Information
235
+ - **Current Price**: ${current_price:.2f}
236
+ - **Data Points**: {len(data)} days
237
+ - **Last Updated**: {data.index[-1].strftime('%Y-%m-%d')}
238
+
239
+ ### Forecast Details
240
+ - **Forecast Period**: {forecast_days} days
241
+ - **Models Used**: {', '.join(model_names)}
242
+ - **End Date**: {future_dates[-1].strftime('%Y-%m-%d')}
243
+
244
+ ### Predicted Prices (Day {forecast_days})
245
+ """
246
+
247
+ for forecast, name in zip(forecasts, model_names):
248
+ final_price = forecast[-1]
249
+ change = ((final_price - current_price) / current_price) * 100
250
+ emoji = "๐Ÿ“ˆ" if change > 0 else "๐Ÿ“‰"
251
+ summary += f"\n{emoji} **{name}**: ${final_price:.2f} ({change:+.2f}%)"
252
+
253
+ summary += f"""
254
+
255
+ ### Average Prediction
256
+ - **Average Price**: ${avg_forecast:.2f}
257
+ - **Expected Change**: {avg_change:+.2f}%
258
+
259
+ ---
260
+ โš ๏ธ **Risk Warning**: Past performance does not guarantee future results. Use for research only.
261
+ """
262
+
263
+ return fig, summary, forecast_df
264
+
265
+ # Create Gradio Interface
266
+ with gr.Blocks(title="Stock Price Forecasting", theme=gr.themes.Soft()) as demo:
267
+ gr.Markdown(
268
+ """
269
+ # ๐Ÿ“ˆ AI Stock Price Forecasting
270
+
271
+ ### Predict future stock prices using multiple time-series models
272
+
273
+ This app trains models **in real-time** using the latest stock data. No pre-trained models needed!
274
+
275
+ **โœจ Features:**
276
+ - Real-time data from Yahoo Finance
277
+ - Multiple forecasting algorithms
278
+ - Interactive visualizations
279
+ - No storage limits - models train on demand
280
+
281
+ ---
282
+ """
283
+ )
284
+
285
+ with gr.Row():
286
+ with gr.Column(scale=1):
287
+ gr.Markdown("### ๐ŸŽฏ Input Parameters")
288
+
289
+ ticker_input = gr.Textbox(
290
+ label="๐Ÿ“Š Stock Ticker Symbol",
291
+ placeholder="e.g., AAPL, GOOGL, TSLA, MSFT",
292
+ value="AAPL",
293
+ info="Enter any valid stock ticker"
294
+ )
295
+
296
+ forecast_days = gr.Slider(
297
+ minimum=7,
298
+ maximum=90,
299
+ value=30,
300
+ step=1,
301
+ label="๐Ÿ“… Forecast Period (Days)",
302
+ info="Number of days to forecast"
303
+ )
304
+
305
+ model_choice = gr.Radio(
306
+ choices=["All Models", "ARIMA", "Prophet", "Exp. Smoothing", "Moving Average"],
307
+ value="All Models",
308
+ label="๐Ÿค– Select Model(s)",
309
+ info="Choose which forecasting model to use"
310
+ )
311
+
312
+ predict_btn = gr.Button(
313
+ "๐Ÿ”ฎ Generate Forecast",
314
+ variant="primary",
315
+ size="lg",
316
+ scale=1
317
+ )
318
+
319
+ gr.Markdown(
320
+ """
321
+ ### ๐Ÿ’ก Quick Tips
322
+ - Use 30 days for short-term
323
+ - Use 60-90 days for trends
324
+ - "All Models" shows comparison
325
+ """
326
+ )
327
+
328
+ with gr.Column(scale=2):
329
+ output_plot = gr.Plot(label="๐Ÿ“ˆ Forecast Visualization")
330
+
331
+ with gr.Row():
332
+ with gr.Column():
333
+ output_summary = gr.Markdown(label="๐Ÿ“‹ Analysis Summary")
334
+
335
+ with gr.Row():
336
+ output_table = gr.Dataframe(
337
+ label="๐Ÿ“Š Detailed Forecast Table",
338
+ wrap=True,
339
+ interactive=False,
340
+ height=400
341
+ )
342
+
343
+ # Examples
344
+ gr.Markdown("### ๐ŸŽฏ Try These Examples")
345
+ gr.Examples(
346
+ examples=[
347
+ ["AAPL", 30, "All Models"],
348
+ ["GOOGL", 14, "Prophet"],
349
+ ["TSLA", 60, "ARIMA"],
350
+ ["MSFT", 45, "Exp. Smoothing"],
351
+ ["NVDA", 30, "All Models"],
352
+ ],
353
+ inputs=[ticker_input, forecast_days, model_choice],
354
+ label="Popular Stocks"
355
+ )
356
+
357
+ # Connect the button to the function
358
+ predict_btn.click(
359
+ fn=predict_stock,
360
+ inputs=[ticker_input, forecast_days, model_choice],
361
+ outputs=[output_plot, output_summary, output_table]
362
+ )
363
+
364
+ gr.Markdown(
365
+ """
366
+ ---
367
+ ## ๐Ÿ“š About the Models
368
+
369
+ | Model | Best For | Speed | Accuracy |
370
+ |-------|----------|-------|----------|
371
+ | **ARIMA** | Short-term, stationary data | โšกโšกโšก Fast | โญโญโญ |
372
+ | **Prophet** | Seasonality, trends | โšกโšก Medium | โญโญโญโญ |
373
+ | **Exp. Smoothing** | Smooth trends | โšกโšกโšก Fast | โญโญโญ |
374
+ | **Moving Average** | Simple baseline | โšกโšกโšกโšก Very Fast | โญโญ |
375
+
376
+ ## โš ๏ธ Important Disclaimer
377
+
378
+ **This tool is for educational and research purposes only.**
379
+
380
+ - Stock predictions are inherently uncertain
381
+ - Past performance โ‰  future results
382
+ - Always do your own research
383
+ - Consult financial advisors before investing
384
+ - Never invest more than you can afford to lose
385
+
386
+ ## ๐Ÿ”’ Privacy & Data
387
+
388
+ - No data is stored permanently
389
+ - Models train fresh for each prediction
390
+ - Stock data fetched from Yahoo Finance API
391
+ - No personal information collected
392
+
393
+ ---
394
+
395
+ **Made with โค๏ธ using Gradio & Python**
396
+ """
397
+ )
398
+
399
+ # Launch the app
400
+ if __name__ == "__main__":
401
+ demo.launch(
402
+ share=False,
403
+ show_error=True,
404
+ server_name="0.0.0.0",
405
+ server_port=7860
406
+ )