Sadat1210 commited on
Commit
97d36e2
ยท
verified ยท
1 Parent(s): 76f28f9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +714 -119
app.py CHANGED
@@ -1,164 +1,759 @@
1
  import gradio as gr
2
  import pandas as pd
3
  import numpy as np
4
- import yfinance as yf
5
  from datetime import datetime, timedelta
 
6
  import plotly.graph_objects as go
7
  from prophet import Prophet
8
- import joblib
 
 
 
 
 
 
 
 
9
 
10
- # Load pre-trained model (Prophet as example)
11
- def load_model():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  try:
13
- model = joblib.load('best_model.pkl')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  return model
15
- except:
 
16
  return None
17
 
18
- def predict_stock_price(ticker, days_to_predict):
19
- """
20
- Main prediction function for Gradio interface
21
- """
 
 
 
 
 
 
 
 
 
 
22
  try:
23
- # Download recent data
24
- end_date = datetime.now()
25
- start_date = end_date - timedelta(days=365*2)
26
 
27
- stock_data = yf.download(ticker, start=start_date, end=end_date)
 
28
 
29
- if stock_data.empty:
30
- return None, "Invalid ticker symbol or no data available"
31
 
32
- # Prepare data for Prophet
33
- df = pd.DataFrame()
34
- df['ds'] = stock_data.index
35
- df['y'] = stock_data['Close'].values
 
 
 
 
36
 
37
- # Train Prophet model
38
- model = Prophet(daily_seasonality=True, yearly_seasonality=True)
39
- model.fit(df)
40
 
41
- # Make predictions
42
- future = model.make_future_dataframe(periods=int(days_to_predict))
43
- forecast = model.predict(future)
 
 
 
 
 
 
 
 
 
 
 
44
 
45
- # Create interactive plot
46
- fig = go.Figure()
 
 
 
 
 
 
47
 
48
- # Historical data
49
- fig.add_trace(go.Scatter(
50
- x=df['ds'],
51
- y=df['y'],
52
- mode='lines',
53
- name='Historical Price',
54
- line=dict(color='blue')
55
- ))
56
 
57
- # Predictions
58
- future_dates = forecast['ds'][len(df):]
59
- future_prices = forecast['yhat'][len(df):]
 
 
 
60
 
61
- fig.add_trace(go.Scatter(
62
- x=future_dates,
63
- y=future_prices,
64
- mode='lines',
65
- name='Predicted Price',
66
- line=dict(color='red', dash='dash')
67
- ))
68
 
69
- # Confidence intervals
70
- fig.add_trace(go.Scatter(
71
- x=future_dates,
72
- y=forecast['yhat_upper'][len(df):],
73
- fill=None,
74
- mode='lines',
75
- line_color='rgba(255,0,0,0)',
76
- showlegend=False
77
- ))
 
 
 
 
 
 
 
78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  fig.add_trace(go.Scatter(
80
  x=future_dates,
81
- y=forecast['yhat_lower'][len(df):],
82
- fill='tonexty',
83
- mode='lines',
84
- line_color='rgba(255,0,0,0)',
85
- name='Confidence Interval',
86
- fillcolor='rgba(255,0,0,0.2)'
87
  ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
- fig.update_layout(
90
- title=f'{ticker} Stock Price Prediction',
91
- xaxis_title='Date',
92
- yaxis_title='Price ($)',
93
- hovermode='x unified'
94
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
- # Prepare summary statistics
97
- last_price = df['y'].iloc[-1]
98
- predicted_price = future_prices.iloc[-1]
99
- price_change = predicted_price - last_price
100
- percent_change = (price_change / last_price) * 100
101
-
102
- summary = f"""
103
- ### Prediction Summary
104
- - **Current Price**: ${last_price:.2f}
105
- - **Predicted Price ({days_to_predict} days)**: ${predicted_price:.2f}
106
- - **Expected Change**: ${price_change:.2f} ({percent_change:+.2f}%)
107
- - **Prediction Date**: {future_dates.iloc[-1].strftime('%Y-%m-%d')}
108
- """
109
 
110
- return fig, summary
 
 
 
 
 
 
 
 
 
111
 
 
 
 
 
112
  except Exception as e:
113
- return None, f"Error: {str(e)}"
 
 
 
 
 
114
 
115
- # Create Gradio interface
116
- with gr.Blocks(title="Stock Price Forecasting - DataSynthis_ML_JobTask") as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  gr.Markdown("""
118
- # ๐Ÿ“ˆ Stock Price Time-Series Forecasting
119
- ### DataSynthis ML Job Task - SET B
120
 
121
- This application uses advanced time-series forecasting models to predict future stock prices.
122
- Enter a stock ticker symbol and the number of days to predict.
 
 
 
123
  """)
124
 
125
- with gr.Row():
126
- with gr.Column(scale=1):
127
- ticker_input = gr.Textbox(
128
- label="Stock Ticker Symbol",
129
- placeholder="e.g., AAPL, GOOGL, MSFT",
130
- value="AAPL"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  )
132
- days_input = gr.Slider(
133
- minimum=1,
134
- maximum=90,
135
- value=30,
136
- step=1,
137
- label="Days to Predict"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  )
139
- predict_btn = gr.Button("๐Ÿ”ฎ Predict", variant="primary")
 
 
 
 
140
 
141
- with gr.Column(scale=2):
142
- output_plot = gr.Plot(label="Price Prediction Chart")
143
- output_summary = gr.Markdown(label="Prediction Summary")
144
-
145
- predict_btn.click(
146
- fn=predict_stock_price,
147
- inputs=[ticker_input, days_input],
148
- outputs=[output_plot, output_summary]
149
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
  gr.Markdown("""
152
  ---
153
- ### Model Information
154
- - **Models Implemented**: ARIMA, LSTM, Prophet
155
- - **Evaluation Metrics**: RMSE, MAE, MAPE
156
- - **Deployment**: Hugging Face Spaces
157
-
158
- ### About
159
- This project implements and compares multiple time-series forecasting approaches for stock price prediction.
160
- The deployed model uses Prophet for its robustness and ability to handle seasonal patterns.
161
  """)
162
 
 
 
 
 
163
  if __name__ == "__main__":
164
- demo.launch()
 
1
  import gradio as gr
2
  import pandas as pd
3
  import numpy as np
 
4
  from datetime import datetime, timedelta
5
+ from plotly.subplots import make_subplots
6
  import plotly.graph_objects as go
7
  from prophet import Prophet
8
+ from statsmodels.tsa.arima.model import ARIMA
9
+ from statsmodels.tsa.stattools import adfuller
10
+ from sklearn.preprocessing import MinMaxScaler
11
+ from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
12
+ import tensorflow as tf
13
+ from tensorflow.keras.models import Sequential
14
+ from tensorflow.keras.layers import LSTM, Dense, Dropout
15
+ import warnings
16
+ warnings.filterwarnings('ignore')
17
 
18
+ np.random.seed(42)
19
+ tf.random.set_seed(42)
20
+
21
+ # ============================================================================
22
+ # DATA LOADING
23
+ # ============================================================================
24
+
25
+ def load_stock_data():
26
+ """Load AAPL stock data from CSV file"""
27
+ try:
28
+ df = pd.read_csv('AAPL_stock_data.csv', parse_dates=['Date'])
29
+ df = df.sort_values('Date').reset_index(drop=True)
30
+ df['Returns'] = df['Close'].pct_change()
31
+
32
+ print(f"Loaded {len(df)} records from AAPL_stock_data.csv")
33
+ print(f"Date Range: {df['Date'].min()} to {df['Date'].max()}")
34
+ return df
35
+ except Exception as e:
36
+ print(f"Error loading CSV: {e}")
37
+ raise Exception("Please upload AAPL_stock_data.csv file")
38
+
39
+ # ============================================================================
40
+ # METRICS CALCULATION
41
+ # ============================================================================
42
+
43
+ def calculate_metrics(y_true, y_pred):
44
+ """Calculate evaluation metrics"""
45
+ rmse = np.sqrt(mean_squared_error(y_true, y_pred))
46
+ mae = mean_absolute_error(y_true, y_pred)
47
+ mape = np.mean(np.abs((y_true - y_pred) / y_true)) * 100
48
+ r2 = r2_score(y_true, y_pred)
49
+ return rmse, mae, mape, r2
50
+
51
+ # ============================================================================
52
+ # STATIONARITY TEST
53
+ # ============================================================================
54
+
55
+ def test_stationarity(timeseries):
56
+ """Perform Augmented Dickey-Fuller test"""
57
+ result = adfuller(timeseries.dropna(), autolag='AIC')
58
+ return {
59
+ 'statistic': result[0],
60
+ 'pvalue': result[1],
61
+ 'is_stationary': result[1] <= 0.05,
62
+ 'critical_values': result[4]
63
+ }
64
+
65
+ # ============================================================================
66
+ # MODEL 1: ARIMA
67
+ # ============================================================================
68
+
69
+ def train_arima_model(data, order=(5,1,0)):
70
+ """Train ARIMA model"""
71
  try:
72
+ print(f"Training ARIMA{order}...")
73
+ model = ARIMA(data, order=order)
74
+ fitted_model = model.fit()
75
+ print("ARIMA trained successfully")
76
+ return fitted_model
77
+ except Exception as e:
78
+ print(f"ARIMA Error: {e}")
79
+ return None
80
+
81
+ # ============================================================================
82
+ # MODEL 2: PROPHET
83
+ # ============================================================================
84
+
85
+ def train_prophet_model(df):
86
+ """Train Prophet model"""
87
+ try:
88
+ print("Training Prophet...")
89
+ prophet_df = pd.DataFrame({
90
+ 'ds': df['Date'],
91
+ 'y': df['Close'].values
92
+ })
93
+
94
+ model = Prophet(
95
+ daily_seasonality=True,
96
+ yearly_seasonality=True,
97
+ weekly_seasonality=True,
98
+ changepoint_prior_scale=0.05,
99
+ seasonality_prior_scale=10.0
100
+ )
101
+ model.fit(prophet_df)
102
+ print("Prophet trained successfully")
103
  return model
104
+ except Exception as e:
105
+ print(f"Prophet Error: {e}")
106
  return None
107
 
108
+ # ============================================================================
109
+ # MODEL 3: LSTM
110
+ # ============================================================================
111
+
112
+ def create_lstm_dataset(data, look_back=60):
113
+ """Create sequences for LSTM"""
114
+ X, y = [], []
115
+ for i in range(look_back, len(data)):
116
+ X.append(data[i-look_back:i, 0])
117
+ y.append(data[i, 0])
118
+ return np.array(X), np.array(y)
119
+
120
+ def train_lstm_model(data, look_back=60, epochs=50):
121
+ """Train LSTM model"""
122
  try:
123
+ print("Training LSTM...")
 
 
124
 
125
+ scaler = MinMaxScaler(feature_range=(0, 1))
126
+ scaled_data = scaler.fit_transform(data.reshape(-1, 1))
127
 
128
+ X, y = create_lstm_dataset(scaled_data, look_back)
129
+ X = X.reshape((X.shape[0], X.shape[1], 1))
130
 
131
+ model = Sequential([
132
+ LSTM(50, return_sequences=True, input_shape=(look_back, 1)),
133
+ Dropout(0.2),
134
+ LSTM(50, return_sequences=False),
135
+ Dropout(0.2),
136
+ Dense(25, activation='relu'),
137
+ Dense(1)
138
+ ])
139
 
140
+ model.compile(optimizer='adam', loss='mean_squared_error')
141
+ model.fit(X, y, batch_size=32, epochs=epochs, verbose=0)
 
142
 
143
+ print("LSTM trained successfully")
144
+ return model, scaler, look_back
145
+ except Exception as e:
146
+ print(f"LSTM Error: {e}")
147
+ return None, None, None
148
+
149
+ # ============================================================================
150
+ # EDA FUNCTION
151
+ # ============================================================================
152
+
153
+ def generate_eda():
154
+ """Generate exploratory data analysis"""
155
+ try:
156
+ df = load_stock_data()
157
 
158
+ # Create subplots
159
+ fig = make_subplots(
160
+ rows=2, cols=2,
161
+ subplot_titles=('Stock Close Price Over Time', 'Trading Volume',
162
+ 'Daily Returns', 'Returns Distribution'),
163
+ specs=[[{"secondary_y": False}, {"secondary_y": False}],
164
+ [{"secondary_y": False}, {"secondary_y": False}]]
165
+ )
166
 
167
+ # Close Price
168
+ fig.add_trace(
169
+ go.Scatter(x=df['Date'], y=df['Close'], mode='lines',
170
+ name='Close Price', line=dict(color='blue', width=1.5)),
171
+ row=1, col=1
172
+ )
 
 
173
 
174
+ # Volume
175
+ fig.add_trace(
176
+ go.Bar(x=df['Date'], y=df['Volume'], name='Volume',
177
+ marker=dict(color='green', opacity=0.6)),
178
+ row=1, col=2
179
+ )
180
 
181
+ # Daily Returns
182
+ fig.add_trace(
183
+ go.Scatter(x=df['Date'], y=df['Returns'], mode='lines',
184
+ name='Returns', line=dict(color='red', width=0.8)),
185
+ row=2, col=1
186
+ )
187
+ fig.add_hline(y=0, line_dash="dash", line_color="black", row=2, col=1)
188
 
189
+ # Returns Distribution
190
+ fig.add_trace(
191
+ go.Histogram(x=df['Returns'].dropna(), nbinsx=50,
192
+ name='Distribution', marker=dict(color='purple', opacity=0.7)),
193
+ row=2, col=2
194
+ )
195
+
196
+ fig.update_layout(height=800, showlegend=False, template='plotly_white')
197
+ fig.update_xaxes(title_text="Date", row=1, col=1)
198
+ fig.update_xaxes(title_text="Date", row=1, col=2)
199
+ fig.update_xaxes(title_text="Date", row=2, col=1)
200
+ fig.update_xaxes(title_text="Returns", row=2, col=2)
201
+ fig.update_yaxes(title_text="Price ($)", row=1, col=1)
202
+ fig.update_yaxes(title_text="Volume", row=1, col=2)
203
+ fig.update_yaxes(title_text="Returns", row=2, col=1)
204
+ fig.update_yaxes(title_text="Frequency", row=2, col=2)
205
 
206
+ # Statistics
207
+ stats = f"""
208
+ ### ๐Ÿ“Š Dataset Statistics
209
+
210
+ **Price Statistics:**
211
+ - Mean Close Price: ${df['Close'].mean():.2f}
212
+ - Median Close Price: ${df['Close'].median():.2f}
213
+ - Min Close Price: ${df['Close'].min():.2f}
214
+ - Max Close Price: ${df['Close'].max():.2f}
215
+ - Standard Deviation: ${df['Close'].std():.2f}
216
+
217
+ **Returns Statistics:**
218
+ - Mean Daily Return: {df['Returns'].mean()*100:.4f}%
219
+ - Volatility (Std): {df['Returns'].std()*100:.4f}%
220
+ - Min Return: {df['Returns'].min()*100:.2f}%
221
+ - Max Return: {df['Returns'].max()*100:.2f}%
222
+
223
+ **Dataset Info:**
224
+ - Total Records: {len(df)}
225
+ - Date Range: {df['Date'].min().strftime('%Y-%m-%d')} to {df['Date'].max().strftime('%Y-%m-%d')}
226
+ - Missing Values: {df.isnull().sum().sum()}
227
+ """
228
+
229
+ # Stationarity test
230
+ stationarity_result = test_stationarity(df['Close'])
231
+
232
+ stationarity_text = f"""
233
+ ### ๐Ÿ”ฌ Stationarity Test (Augmented Dickey-Fuller)
234
+
235
+ - **ADF Statistic:** {stationarity_result['statistic']:.6f}
236
+ - **p-value:** {stationarity_result['pvalue']:.6f}
237
+ - **Result:** {'โœ… Series is STATIONARY' if stationarity_result['is_stationary'] else 'โŒ Series is NON-STATIONARY'}
238
+
239
+ **Critical Values:**
240
+ """
241
+ for key, value in stationarity_result['critical_values'].items():
242
+ stationarity_text += f"- {key}: {value:.3f}\n"
243
+
244
+ return fig, stats, stationarity_text
245
+
246
+ except Exception as e:
247
+ error_msg = f"### โŒ Error\n\n{str(e)}"
248
+ return None, error_msg, error_msg
249
+
250
+ # ============================================================================
251
+ # ROLLING WINDOW EVALUATION
252
+ # ============================================================================
253
+
254
+ def rolling_window_evaluation(df, test_size=90):
255
+ """Perform rolling window evaluation for all models"""
256
+
257
+ print("\n" + "="*70)
258
+ print("RUNNING MODEL EVALUATION")
259
+ print("="*70)
260
+
261
+ train_data = df[:-test_size].copy()
262
+ test_data = df[-test_size:].copy()
263
+
264
+ print(f"Training set: {len(train_data)} days")
265
+ print(f"Test set: {len(test_data)} days")
266
+
267
+ results = {
268
+ 'ARIMA': {'predictions': [], 'metrics': {}},
269
+ 'LSTM': {'predictions': [], 'metrics': {}},
270
+ 'Prophet': {'predictions': [], 'metrics': {}}
271
+ }
272
+
273
+ # ARIMA
274
+ arima_model = train_arima_model(train_data['Close'].values)
275
+ if arima_model:
276
+ arima_forecast = arima_model.forecast(steps=test_size)
277
+ results['ARIMA']['predictions'] = arima_forecast
278
+ rmse, mae, mape, r2 = calculate_metrics(test_data['Close'].values, arima_forecast)
279
+ results['ARIMA']['metrics'] = {'RMSE': rmse, 'MAE': mae, 'MAPE': mape, 'R2': r2}
280
+ print(f"ARIMA - RMSE: {rmse:.2f}, MAE: {mae:.2f}, MAPE: {mape:.2f}%, R2: {r2:.4f}")
281
+
282
+ # Prophet
283
+ prophet_model = train_prophet_model(train_data)
284
+ if prophet_model:
285
+ future = prophet_model.make_future_dataframe(periods=test_size)
286
+ forecast = prophet_model.predict(future)
287
+ prophet_forecast = forecast['yhat'].iloc[-test_size:].values
288
+ results['Prophet']['predictions'] = prophet_forecast
289
+ rmse, mae, mape, r2 = calculate_metrics(test_data['Close'].values, prophet_forecast)
290
+ results['Prophet']['metrics'] = {'RMSE': rmse, 'MAE': mae, 'MAPE': mape, 'R2': r2}
291
+ print(f"Prophet - RMSE: {rmse:.2f}, MAE: {mae:.2f}, MAPE: {mape:.2f}%, R2: {r2:.4f}")
292
+
293
+ # LSTM
294
+ lstm_model, scaler, look_back = train_lstm_model(train_data['Close'].values, epochs=50)
295
+ if lstm_model and scaler:
296
+ all_data = df['Close'].values
297
+ test_inputs = all_data[len(train_data) - look_back:]
298
+ test_inputs = scaler.transform(test_inputs.reshape(-1, 1))
299
+
300
+ X_test = []
301
+ for i in range(look_back, len(test_inputs)):
302
+ X_test.append(test_inputs[i-look_back:i, 0])
303
+ X_test = np.array(X_test)
304
+ X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 1))
305
+
306
+ lstm_predictions = lstm_model.predict(X_test, verbose=0)
307
+ lstm_predictions = scaler.inverse_transform(lstm_predictions)
308
+ results['LSTM']['predictions'] = lstm_predictions.flatten()
309
+
310
+ rmse, mae, mape, r2 = calculate_metrics(test_data['Close'].values, lstm_predictions.flatten())
311
+ results['LSTM']['metrics'] = {'RMSE': rmse, 'MAE': mae, 'MAPE': mape, 'R2': r2}
312
+ print(f"LSTM - RMSE: {rmse:.2f}, MAE: {mae:.2f}, MAPE: {mape:.2f}%, R2: {r2:.4f}")
313
+
314
+ print("="*70 + "\n")
315
+ return results, train_data, test_data
316
+
317
+ # ============================================================================
318
+ # FUTURE PREDICTIONS
319
+ # ============================================================================
320
+
321
+ def predict_future(df, days_to_predict=30):
322
+ """Make future predictions with all three models"""
323
+
324
+ print("\n" + "="*70)
325
+ print(f"GENERATING {days_to_predict}-DAY PREDICTIONS")
326
+ print("="*70)
327
+
328
+ full_data = df['Close'].values
329
+ predictions = {}
330
+
331
+ # ARIMA
332
+ arima_model = train_arima_model(full_data)
333
+ if arima_model:
334
+ arima_forecast = arima_model.forecast(steps=days_to_predict)
335
+ predictions['ARIMA'] = arima_forecast
336
+
337
+ # Prophet
338
+ prophet_model = train_prophet_model(df)
339
+ if prophet_model:
340
+ future = prophet_model.make_future_dataframe(periods=days_to_predict)
341
+ forecast = prophet_model.predict(future)
342
+ predictions['Prophet'] = forecast['yhat'].iloc[-days_to_predict:].values
343
+
344
+ # LSTM
345
+ lstm_model, scaler, look_back = train_lstm_model(full_data, epochs=50)
346
+ if lstm_model and scaler:
347
+ last_sequence = scaler.transform(full_data[-look_back:].reshape(-1, 1))
348
+ lstm_predictions = []
349
+
350
+ for _ in range(days_to_predict):
351
+ X_test = last_sequence.reshape((1, look_back, 1))
352
+ pred = lstm_model.predict(X_test, verbose=0)
353
+ lstm_predictions.append(pred[0, 0])
354
+ last_sequence = np.append(last_sequence[1:], pred).reshape(-1, 1)
355
+
356
+ lstm_predictions = scaler.inverse_transform(np.array(lstm_predictions).reshape(-1, 1))
357
+ predictions['LSTM'] = lstm_predictions.flatten()
358
+
359
+ print("="*70 + "\n")
360
+ return predictions
361
+
362
+ # ============================================================================
363
+ # VISUALIZATION FUNCTIONS
364
+ # ============================================================================
365
+
366
+ def create_comparison_plot(results, train_data, test_data):
367
+ """Create comparison plot for model evaluation"""
368
+ fig = go.Figure()
369
+
370
+ fig.add_trace(go.Scatter(
371
+ x=train_data['Date'],
372
+ y=train_data['Close'],
373
+ mode='lines',
374
+ name='Training Data',
375
+ line=dict(color='blue', width=1.5)
376
+ ))
377
+
378
+ fig.add_trace(go.Scatter(
379
+ x=test_data['Date'],
380
+ y=test_data['Close'],
381
+ mode='lines',
382
+ name='Actual Test Data',
383
+ line=dict(color='green', width=3)
384
+ ))
385
+
386
+ colors = {'ARIMA': 'red', 'LSTM': 'orange', 'Prophet': 'purple'}
387
+
388
+ for model_name in ['ARIMA', 'LSTM', 'Prophet']:
389
+ if len(results[model_name]['predictions']) > 0:
390
+ fig.add_trace(go.Scatter(
391
+ x=test_data['Date'],
392
+ y=results[model_name]['predictions'],
393
+ mode='lines',
394
+ name=f'{model_name} Prediction',
395
+ line=dict(color=colors[model_name], dash='dash', width=2)
396
+ ))
397
+
398
+ fig.update_layout(
399
+ title='AAPL Stock Price - Model Comparison (Rolling Window Evaluation)',
400
+ xaxis_title='Date',
401
+ yaxis_title='Stock Price ($)',
402
+ hovermode='x unified',
403
+ height=600,
404
+ template='plotly_white',
405
+ legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1)
406
+ )
407
+
408
+ return fig
409
+
410
+ def create_future_plot(df, predictions, days_to_predict):
411
+ """Create plot for future predictions"""
412
+ fig = go.Figure()
413
+
414
+ recent_data = df.tail(180)
415
+ fig.add_trace(go.Scatter(
416
+ x=recent_data['Date'],
417
+ y=recent_data['Close'],
418
+ mode='lines',
419
+ name='Historical Data',
420
+ line=dict(color='blue', width=2)
421
+ ))
422
+
423
+ last_date = df['Date'].iloc[-1]
424
+ future_dates = pd.date_range(start=last_date + timedelta(days=1), periods=days_to_predict)
425
+
426
+ colors = {'ARIMA': 'red', 'LSTM': 'orange', 'Prophet': 'purple'}
427
+
428
+ for model_name, preds in predictions.items():
429
  fig.add_trace(go.Scatter(
430
  x=future_dates,
431
+ y=preds,
432
+ mode='lines+markers',
433
+ name=f'{model_name} Forecast',
434
+ line=dict(color=colors[model_name], dash='dash', width=2),
435
+ marker=dict(size=4)
 
436
  ))
437
+
438
+ fig.update_layout(
439
+ title=f'AAPL Stock Price Forecast - Next {days_to_predict} Days',
440
+ xaxis_title='Date',
441
+ yaxis_title='Stock Price ($)',
442
+ hovermode='x unified',
443
+ height=600,
444
+ template='plotly_white',
445
+ legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1)
446
+ )
447
+
448
+ return fig
449
+
450
+ # ============================================================================
451
+ # FORMATTING FUNCTIONS
452
+ # ============================================================================
453
+
454
+ def format_metrics_table(results):
455
+ """Format metrics as markdown table"""
456
+ table = "### ๐Ÿ“Š Model Performance Comparison\n\n"
457
+ table += "| Model | RMSE | MAE | MAPE (%) | Rยฒ Score |\n"
458
+ table += "|-------|------|-----|----------|----------|\n"
459
+
460
+ for model_name in ['ARIMA', 'LSTM', 'Prophet']:
461
+ metrics = results[model_name]['metrics']
462
+ if metrics:
463
+ table += f"| **{model_name}** | {metrics['RMSE']:.2f} | {metrics['MAE']:.2f} | {metrics['MAPE']:.2f} | {metrics['R2']:.4f} |\n"
464
+
465
+ best_model = min(
466
+ results.keys(),
467
+ key=lambda x: results[x]['metrics'].get('RMSE', float('inf')) if results[x]['metrics'] else float('inf')
468
+ )
469
+
470
+ table += f"\n### ๐Ÿ† Best Model: **{best_model}** (Lowest RMSE)\n\n"
471
+
472
+ table += "#### Model Characteristics:\n\n"
473
+ table += "- **ARIMA**: Statistical model, captures linear trends and autocorrelation\n"
474
+ table += "- **LSTM**: Deep learning model, captures complex non-linear patterns\n"
475
+ table += "- **Prophet**: Hybrid model, robust to missing data and seasonality\n\n"
476
+
477
+ table += "#### Evaluation Metrics:\n\n"
478
+ table += "- **RMSE** (Root Mean Squared Error): Lower is better, penalizes large errors\n"
479
+ table += "- **MAE** (Mean Absolute Error): Average prediction error in dollars\n"
480
+ table += "- **MAPE** (Mean Absolute Percentage Error): Prediction error as percentage\n"
481
+ table += "- **Rยฒ** (R-squared): Proportion of variance explained (closer to 1 is better)\n"
482
+
483
+ return table
484
+
485
+ def format_prediction_summary(df, predictions, days_to_predict):
486
+ """Format prediction summary"""
487
+ current_price = df['Close'].iloc[-1]
488
+ current_date = df['Date'].iloc[-1].strftime('%Y-%m-%d')
489
+
490
+ summary = f"### ๐Ÿ“ˆ AAPL Stock Price Predictions ({days_to_predict} days ahead)\n\n"
491
+ summary += f"**Current Price** (as of {current_date}): **${current_price:.2f}**\n\n"
492
+ summary += "| Model | Predicted Price | Change ($) | Change (%) |\n"
493
+ summary += "|-------|----------------|------------|------------|\n"
494
+
495
+ for model_name, preds in predictions.items():
496
+ pred_price = preds[-1]
497
+ change = pred_price - current_price
498
+ pct_change = (change / current_price) * 100
499
 
500
+ emoji = "๐Ÿ“ˆ" if change > 0 else "๐Ÿ“‰"
501
+ summary += f"| **{model_name}** {emoji} | ${pred_price:.2f} | ${change:+.2f} | {pct_change:+.2f}% |\n"
502
+
503
+ avg_prediction = np.mean([preds[-1] for preds in predictions.values()])
504
+ avg_change = avg_prediction - current_price
505
+ avg_pct_change = (avg_change / current_price) * 100
506
+
507
+ summary += f"\n**Average Prediction**: ${avg_prediction:.2f} ({avg_pct_change:+.2f}%)\n\n"
508
+
509
+ summary += "#### โš ๏ธ Disclaimer\n"
510
+ summary += "*These predictions are for educational purposes only. Stock market predictions are inherently uncertain. "
511
+ summary += "Past performance does not guarantee future results. Always consult with financial advisors before making investment decisions.*\n"
512
+
513
+ return summary
514
+
515
+ # ============================================================================
516
+ # MAIN INTERFACE FUNCTIONS
517
+ # ============================================================================
518
+
519
+ def run_evaluation():
520
+ """Run model evaluation"""
521
+ try:
522
+ df = load_stock_data()
523
+ results, train_data, test_data = rolling_window_evaluation(df, test_size=90)
524
 
525
+ plot = create_comparison_plot(results, train_data, test_data)
526
+ metrics_table = format_metrics_table(results)
 
 
 
 
 
 
 
 
 
 
 
527
 
528
+ return plot, metrics_table
529
+ except Exception as e:
530
+ error_msg = f"### โŒ Error\n\n{str(e)}\n\nPlease make sure AAPL_stock_data.csv is uploaded."
531
+ return None, error_msg
532
+
533
+ def run_prediction(days_to_predict):
534
+ """Run future prediction"""
535
+ try:
536
+ df = load_stock_data()
537
+ predictions = predict_future(df, int(days_to_predict))
538
 
539
+ plot = create_future_plot(df, predictions, int(days_to_predict))
540
+ summary = format_prediction_summary(df, predictions, int(days_to_predict))
541
+
542
+ return plot, summary
543
  except Exception as e:
544
+ error_msg = f"### โŒ Error\n\n{str(e)}\n\nPlease make sure AAPL_stock_data.csv is uploaded."
545
+ return None, error_msg
546
+
547
+ # ============================================================================
548
+ # GRADIO INTERFACE
549
+ # ============================================================================
550
 
551
+ custom_css = """
552
+ .gradio-container {
553
+ font-family: 'Arial', sans-serif;
554
+ }
555
+ .main-header {
556
+ text-align: center;
557
+ padding: 20px;
558
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
559
+ color: white;
560
+ border-radius: 10px;
561
+ margin-bottom: 20px;
562
+ }
563
+ """
564
+
565
+ with gr.Blocks(title="Stock Price Forecasting - DataSynthis_ML_JobTask", theme=gr.themes.Soft(), css=custom_css) as demo:
566
+
567
+ gr.Markdown("""
568
+ <div class="main-header">
569
+ <h1>๐Ÿ“ˆ AAPL Stock Price Time-Series Forecasting</h1>
570
+ <h3>DataSynthis ML Job Task - SET B</h3>
571
+ </div>
572
+ """)
573
+
574
  gr.Markdown("""
575
+ This application implements and compares **three time-series forecasting models** for Apple Inc. (AAPL) stock price prediction:
 
576
 
577
+ - ๐Ÿ”ด **ARIMA** (AutoRegressive Integrated Moving Average) - Traditional statistical model
578
+ - ๐ŸŸ  **LSTM** (Long Short-Term Memory) - Deep learning neural network
579
+ - ๐ŸŸฃ **Prophet** - Facebook's robust forecasting tool
580
+
581
+ ---
582
  """)
583
 
584
+ with gr.Tabs() as tabs:
585
+
586
+ # TAB 1: Exploratory Data Analysis
587
+ with gr.Tab("๐Ÿ“Š Exploratory Data Analysis"):
588
+ gr.Markdown("""
589
+ ### Dataset Overview & Statistical Analysis
590
+
591
+ Explore the AAPL stock price data with comprehensive visualizations and statistical summaries.
592
+ """)
593
+
594
+ eda_btn = gr.Button("๐Ÿ” Generate EDA Report", variant="primary", size="lg")
595
+
596
+ eda_plot = gr.Plot(label="EDA Visualizations")
597
+
598
+ with gr.Row():
599
+ with gr.Column():
600
+ eda_stats = gr.Markdown()
601
+ with gr.Column():
602
+ eda_stationarity = gr.Markdown()
603
+
604
+ eda_btn.click(
605
+ fn=generate_eda,
606
+ inputs=[],
607
+ outputs=[eda_plot, eda_stats, eda_stationarity]
608
  )
609
+
610
+ # TAB 2: Future Prediction
611
+ with gr.Tab("๐Ÿ”ฎ Future Prediction"):
612
+ gr.Markdown("""
613
+ ### Predict Future Stock Prices
614
+ Select the number of days you want to predict and click **Generate Predictions** to see forecasts from all three models.
615
+ """)
616
+
617
+ with gr.Row():
618
+ with gr.Column(scale=1):
619
+ days_slider = gr.Slider(
620
+ minimum=7,
621
+ maximum=90,
622
+ value=30,
623
+ step=1,
624
+ label="๐Ÿ“… Days to Predict",
625
+ info="Choose between 7 and 90 days"
626
+ )
627
+ predict_btn = gr.Button("๐Ÿš€ Generate Predictions", variant="primary", size="lg")
628
+
629
+ gr.Markdown("#### Quick Options:")
630
+
631
+ with gr.Row():
632
+ btn_7 = gr.Button("1 Week", size="sm")
633
+ btn_30 = gr.Button("1 Month", size="sm")
634
+ btn_60 = gr.Button("2 Months", size="sm")
635
+ btn_90 = gr.Button("3 Months", size="sm")
636
+
637
+ btn_7.click(lambda: 7, outputs=days_slider)
638
+ btn_30.click(lambda: 30, outputs=days_slider)
639
+ btn_60.click(lambda: 60, outputs=days_slider)
640
+ btn_90.click(lambda: 90, outputs=days_slider)
641
+
642
+ prediction_plot = gr.Plot(label="๐Ÿ“Š Forecast Chart")
643
+ prediction_summary = gr.Markdown()
644
+
645
+ predict_btn.click(
646
+ fn=run_prediction,
647
+ inputs=[days_slider],
648
+ outputs=[prediction_plot, prediction_summary]
649
  )
650
+
651
+ # TAB 3: Model Evaluation
652
+ with gr.Tab("๐Ÿ“Š Model Evaluation"):
653
+ gr.Markdown("""
654
+ ### Rolling Window Evaluation
655
 
656
+ This evaluation uses the **last 90 days** as test data to compare model performance.
657
+
658
+ **Evaluation Metrics:**
659
+ - **RMSE**: Root Mean Squared Error (lower is better)
660
+ - **MAE**: Mean Absolute Error (lower is better)
661
+ - **MAPE**: Mean Absolute Percentage Error (lower is better)
662
+ - **Rยฒ**: R-squared score (closer to 1 is better)
663
+
664
+ Click the button below to run the evaluation:
665
+ """)
666
+
667
+ eval_btn = gr.Button("๐Ÿ”ฌ Run Evaluation", variant="primary", size="lg")
668
+
669
+ evaluation_plot = gr.Plot(label="๐Ÿ“Š Model Comparison Chart")
670
+ metrics_output = gr.Markdown()
671
+
672
+ eval_btn.click(
673
+ fn=run_evaluation,
674
+ inputs=[],
675
+ outputs=[evaluation_plot, metrics_output]
676
+ )
677
+
678
+ # TAB 3: About
679
+ with gr.Tab("โ„น๏ธ About"):
680
+ gr.Markdown("""
681
+ ## ๐Ÿ“ Project Information
682
+
683
+ ### Models Implemented
684
+
685
+ #### 1๏ธโƒฃ ARIMA (5,1,0)
686
+ - **Type**: Statistical time-series model
687
+ - **Strengths**: Fast, interpretable, good for linear trends
688
+ - **Use Case**: Short-term predictions, linear patterns
689
+ - **Training Time**: < 1 second
690
+
691
+ #### 2๏ธโƒฃ LSTM Neural Network
692
+ - **Type**: Deep learning recurrent neural network
693
+ - **Architecture**: 2 LSTM layers (50 units each) with dropout
694
+ - **Strengths**: Captures complex non-linear patterns
695
+ - **Use Case**: Medium to long-term predictions
696
+ - **Training Time**: ~30-60 seconds
697
+
698
+ #### 3๏ธโƒฃ Prophet
699
+ - **Type**: Additive regression model by Facebook
700
+ - **Strengths**: Handles seasonality, robust to missing data
701
+ - **Use Case**: Business forecasting, interpretable components
702
+ - **Training Time**: ~5-10 seconds
703
+
704
+ ---
705
+
706
+ ### Dataset Information
707
+ - **Stock**: Apple Inc. (AAPL)
708
+ - **Features**: Date, Open, High, Low, Close, Volume
709
+ - **Target Variable**: Close Price
710
+ - **Data Source**: Historical stock data (CSV)
711
+
712
+ ---
713
+
714
+ ### Technical Stack
715
+ - **Framework**: Gradio 4.44.0
716
+ - **Deep Learning**: TensorFlow 2.15.0 / Keras
717
+ - **Statistical Models**: Statsmodels 0.14.0
718
+ - **Forecasting**: Prophet 1.1.5
719
+ - **Data Processing**: Pandas, NumPy, Scikit-learn
720
+ - **Visualization**: Plotly
721
+
722
+ ---
723
+
724
+ ### Deployment
725
+ - **Platform**: Hugging Face Spaces
726
+ - **Space Name**: DataSynthis_ML_JobTask
727
+
728
+
729
+
730
+
731
+ ---
732
+
733
+ ### ๐Ÿ‘จโ€๐Ÿ’ป Author
734
+ **Md. Sadat Ahmed Rafi**
735
+ Task: Time-Series Forecasting โ€“ Stock Prices
736
+
737
+
738
+ ---
739
+
740
+ ### ๐Ÿ“„ License
741
+ This project is created for the DataSynthis ML Job Task evaluation.
742
+
743
+ """)
744
 
745
  gr.Markdown("""
746
  ---
747
+ <div style="text-align: center; color: #666; padding: 20px;">
748
+ <p><strong>DataSynthis ML Job Task - SET B</strong></p>
749
+ <p>Time-Series Forecasting โ€“ Stock Prices</p>
750
+ <p>Deployed on Hugging Face Spaces | Powered by Gradio</p>
751
+ </div>
 
 
 
752
  """)
753
 
754
+ # ============================================================================
755
+ # LAUNCH
756
+ # ============================================================================
757
+
758
  if __name__ == "__main__":
759
+ demo.launch()