Spaces:
Sleeping
Sleeping
Download app.py from saibhossain/DataSynthis_ML_JobTask: direct link, hf CLI and curl.
- Browser
- Download file 11.5 kB
-
https://huggingface.co/spaces/saibhossain/DataSynthis_ML_JobTask/resolve/main/app.py
- Command line
-
hf download hf://spaces/saibhossain/DataSynthis_ML_JobTask/app.py
-
curl -L -o app.py https://huggingface.co/spaces/saibhossain/DataSynthis_ML_JobTask/resolve/main/app.py
11.5 kB
| import os | |
| import warnings | |
| warnings.filterwarnings("ignore") | |
| import numpy as np | |
| import pandas as pd | |
| import matplotlib.pyplot as plt | |
| import gradio as gr | |
| from sklearn.preprocessing import MinMaxScaler | |
| from sklearn.metrics import mean_squared_error, mean_absolute_percentage_error | |
| import joblib | |
| from statsmodels.tsa.arima.model import ARIMA | |
| # Prophet can be heavy; we load lazily inside functions to avoid slow import on startup | |
| try: | |
| from prophet import Prophet | |
| PROPHET_AVAILABLE = True | |
| except Exception: | |
| PROPHET_AVAILABLE = False | |
| # TensorFlow (CPU) for LSTM | |
| os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2" | |
| try: | |
| from tensorflow.keras.models import load_model | |
| TENSORFLOW_AVAILABLE = True | |
| except Exception: | |
| TENSORFLOW_AVAILABLE = False | |
| # ---------------------------- | |
| # Load data & artifacts | |
| # ---------------------------- | |
| DATA_PATH = "data/AAPL_stock_2015_2025.csv" | |
| df = pd.read_csv(DATA_PATH) | |
| df["Date"] = pd.to_datetime(df["Date"]) | |
| df = df.sort_values("Date").set_index("Date") | |
| features = ['Open','High','Low','Close','Volume'] | |
| for c in features: | |
| df[c] = pd.to_numeric(df[c], errors="coerce") | |
| df = df.dropna() | |
| series_close = df[["Close"]].copy() | |
| # models | |
| ARIMA_PATH = "models/arima_model.pkl" | |
| PROPHET_PATH = "models/prophet_model.pkl" | |
| LSTM_PATH = "models/lstm_model.h5" | |
| arima_model = None | |
| prophet_model = None | |
| lstm_model = None | |
| if os.path.exists(ARIMA_PATH): | |
| try: | |
| arima_model = joblib.load(ARIMA_PATH) | |
| except Exception: | |
| arima_model = None | |
| if PROPHET_AVAILABLE and os.path.exists(PROPHET_PATH): | |
| try: | |
| prophet_model = joblib.load(PROPHET_PATH) | |
| except Exception: | |
| prophet_model = None | |
| if TENSORFLOW_AVAILABLE: | |
| print("✅ TensorFlow imported successfully") | |
| print("🔎 Checking for LSTM model at:", LSTM_PATH) | |
| if os.path.exists(LSTM_PATH): | |
| try: | |
| lstm_model = load_model(LSTM_PATH, compile=False) | |
| print("✅ LSTM model loaded successfully") | |
| except Exception as e: | |
| print("❌ LSTM load failed:", e) | |
| lstm_model = None | |
| else: | |
| print("❌ LSTM model file not found!") | |
| # ---------------------------- | |
| # Helpers | |
| # ---------------------------- | |
| def plot_series_with_forecasts(history_df, forecast_dict, title): | |
| plt.figure(figsize=(12,5)) | |
| plt.plot(history_df.index, history_df.values, label="Actual", linewidth=1.5) | |
| for name, (idx, vals) in forecast_dict.items(): | |
| plt.plot(idx, vals, label=name, linewidth=1.5) | |
| plt.title(title) | |
| plt.xlabel("Date") | |
| plt.ylabel("Close (USD)") | |
| plt.legend() | |
| plt.tight_layout() | |
| return plt.gcf() | |
| def backtest_split_last_n(n_days): | |
| """Return train/test splits for backtest using last n days as test.""" | |
| train = series_close.iloc[:-n_days, 0] | |
| test = series_close.iloc[-n_days:, 0] | |
| return train, test | |
| def safe_mape(y_true, y_pred): | |
| return mean_absolute_percentage_error(y_true, y_pred) | |
| # ---------------------------- | |
| # ARIMA | |
| # ---------------------------- | |
| def forecast_arima(horizon, retrain_if_needed=True): | |
| global arima_model | |
| if arima_model is None and retrain_if_needed: | |
| # fit a reasonable default quickly | |
| model = ARIMA(series_close.squeeze(), order=(5,1,0)) | |
| arima_model = model.fit() | |
| if arima_model is None: | |
| raise RuntimeError("ARIMA model not available and retrain disabled.") | |
| fc = arima_model.forecast(steps=horizon) | |
| idx = pd.date_range(series_close.index[-1] + pd.Timedelta(days=1), periods=horizon, freq="D") | |
| return idx, fc.values | |
| def backtest_arima(n_days, retrain_if_needed=True): | |
| train, test = backtest_split_last_n(n_days) | |
| model = ARIMA(train, order=(5,1,0)) | |
| fit = model.fit() | |
| pred = fit.forecast(steps=len(test)).values | |
| rmse = np.sqrt(mean_squared_error(test.values, pred)) | |
| mape = safe_mape(test.values, pred) | |
| idx = test.index | |
| return idx, pred, rmse, mape | |
| # ---------------------------- | |
| # Prophet | |
| # ---------------------------- | |
| def forecast_prophet(horizon, retrain_if_needed=True): | |
| global prophet_model | |
| if not PROPHET_AVAILABLE: | |
| raise RuntimeError("Prophet not installed in this Space.") | |
| if prophet_model is None and retrain_if_needed: | |
| p_df = df.reset_index()[["Date","Close"]] | |
| p_df.columns = ["ds","y"] | |
| model = Prophet(daily_seasonality=True) | |
| model.fit(p_df) | |
| prophet_model = model | |
| if prophet_model is None: | |
| raise RuntimeError("Prophet model not available and retrain disabled.") | |
| future = pd.date_range(series_close.index[-1] + pd.Timedelta(days=1), periods=horizon, freq="D") | |
| future_df = pd.DataFrame({"ds": future}) | |
| forecast = prophet_model.predict(future_df) | |
| return future, forecast["yhat"].values | |
| def backtest_prophet(n_days, retrain_if_needed=True): | |
| if not PROPHET_AVAILABLE: | |
| raise RuntimeError("Prophet not installed in this Space.") | |
| p_df = df.reset_index()[["Date","Close"]] | |
| p_df.columns = ["ds","y"] | |
| train = p_df.iloc[:-n_days] | |
| test = p_df.iloc[-n_days:] | |
| model = Prophet(daily_seasonality=True) | |
| model.fit(train) | |
| future = model.make_future_dataframe(periods=len(test)) | |
| fc = model.predict(future).tail(len(test))["yhat"].values | |
| rmse = np.sqrt(mean_squared_error(test["y"].values, fc)) | |
| mape = safe_mape(test["y"].values, fc) | |
| return test["ds"].values, fc, rmse, mape | |
| # ---------------------------- | |
| # LSTM (multivariate) – recursive forecast with naive covariates | |
| # ---------------------------- | |
| WINDOW = 60 | |
| def prepare_scaled_features(): | |
| scaler = MinMaxScaler().fit(df[features].values) | |
| scaled = scaler.transform(df[features].values) | |
| return scaler, scaled | |
| def forecast_lstm(horizon): | |
| if not (TENSORFLOW_AVAILABLE and lstm_model is not None): | |
| raise RuntimeError("LSTM model not available in this Space.") | |
| scaler, scaled = prepare_scaled_features() | |
| window = scaled[-WINDOW:].copy() | |
| preds_scaled_close = [] | |
| # naive strategy | |
| for _ in range(horizon): | |
| x = np.expand_dims(window, axis=0) # (1, 60, 5) | |
| pred_scaled_close = lstm_model.predict(x, verbose=0)[0,0] | |
| preds_scaled_close.append(pred_scaled_close) | |
| next_vec = window[-1].copy() | |
| next_vec[3] = pred_scaled_close # Close | |
| next_vec[0] = pred_scaled_close # Open ~ Close | |
| next_vec[1] = pred_scaled_close # High ~ Close | |
| next_vec[2] = pred_scaled_close # Low ~ Close | |
| # Volume unchanged | |
| window = np.vstack([window[1:], next_vec]) | |
| # inverse-transform Close | |
| dummy = np.zeros((len(preds_scaled_close), len(features))) | |
| dummy[:,3] = np.array(preds_scaled_close) | |
| preds_close = scaler.inverse_transform(dummy)[:,3] | |
| idx = pd.date_range(series_close.index[-1] + pd.Timedelta(days=1), periods=horizon, freq="D") | |
| return idx, preds_close | |
| def backtest_lstm(n_days): | |
| if not (TENSORFLOW_AVAILABLE and lstm_model is not None): | |
| raise RuntimeError("LSTM model not available in this Space.") | |
| scaler, scaled = prepare_scaled_features() | |
| test_scaled = scaled[-(n_days + WINDOW):] | |
| preds = [] | |
| gts = [] | |
| for i in range(WINDOW, len(test_scaled)): | |
| window = test_scaled[i-WINDOW:i].copy() | |
| x = np.expand_dims(window, axis=0) | |
| pred_scaled_close = lstm_model.predict(x, verbose=0)[0,0] | |
| preds.append(pred_scaled_close) | |
| gts.append(test_scaled[i,3]) # true close (scaled) | |
| preds = np.array(preds) | |
| gts = np.array(gts) | |
| # inverse-transform Close | |
| dummy_p = np.zeros((len(preds), len(features))); dummy_p[:,3] = preds | |
| dummy_t = np.zeros((len(gts), len(features))); dummy_t[:,3] = gts | |
| pred_close = scaler.inverse_transform(dummy_p)[:,3] | |
| true_close = scaler.inverse_transform(dummy_t)[:,3] | |
| rmse = np.sqrt(mean_squared_error(true_close, pred_close)) | |
| mape = safe_mape(true_close, pred_close) | |
| idx = df.index[-n_days:] | |
| return idx, pred_close[-n_days:], rmse, mape | |
| # ---------------------------- | |
| # Gradio UI | |
| # ---------------------------- | |
| def do_forecast(horizon, arima, prophet, lstm): | |
| forecasts = {} | |
| messages = [] | |
| if arima: | |
| try: | |
| idx, vals = forecast_arima(horizon) | |
| forecasts["ARIMA"] = (idx, vals) | |
| except Exception as e: | |
| messages.append(f"ARIMA error: {e}") | |
| if prophet: | |
| try: | |
| idx, vals = forecast_prophet(horizon) | |
| forecasts["Prophet"] = (idx, vals) | |
| except Exception as e: | |
| messages.append(f"Prophet error: {e}") | |
| if lstm: | |
| try: | |
| idx, vals = forecast_lstm(horizon) | |
| forecasts["LSTM (Multivariate)"] = (idx, vals) | |
| except Exception as e: | |
| messages.append(f"LSTM error: {e}") | |
| if not forecasts: | |
| return None, "No model produced a forecast. Check build logs or dependencies." | |
| fig = plot_series_with_forecasts(series_close, forecasts, f"Forecast {horizon} days ahead") | |
| return fig, "\n".join(messages) if messages else "OK" | |
| def do_backtest(test_days, arima, prophet, lstm): | |
| rows = [] | |
| overlays = {} | |
| if arima: | |
| try: | |
| idx, pred, rmse, mape = backtest_arima(test_days) | |
| overlays["ARIMA"] = (idx, pred) | |
| rows.append(["ARIMA", rmse, mape]) | |
| except: | |
| rows.append(["ARIMA", None, None]) | |
| if prophet: | |
| try: | |
| idx, pred, rmse, mape = backtest_prophet(test_days) | |
| overlays["Prophet"] = (pd.to_datetime(idx), pred) | |
| rows.append(["Prophet", rmse, mape]) | |
| except: | |
| rows.append(["Prophet", None, None]) | |
| if lstm: | |
| try: | |
| idx, pred, rmse, mape = backtest_lstm(test_days) | |
| overlays["LSTM (Multivariate)"] = (idx, pred) | |
| rows.append(["LSTM (Multivariate)", rmse, mape]) | |
| except: | |
| rows.append(["LSTM (Multivariate)", None, None]) | |
| hist = series_close.iloc[-test_days:] | |
| fig = plot_series_with_forecasts(hist, overlays, f"Backtest on last {test_days} days") | |
| table = pd.DataFrame(rows, columns=["Model", "RMSE", "MAPE"]) | |
| return fig, table | |
| with gr.Blocks(title="DataSynthis_ML_JobTask") as demo: | |
| gr.Markdown("# Multivariate Stock Forecasting (AAPL)\nCompare ARIMA, Prophet, and LSTM.") | |
| with gr.Tab("Forecast"): | |
| with gr.Row(): | |
| horizon = gr.Slider(7, 90, value=30, step=1, label="Forecast horizon (days)") | |
| with gr.Row(): | |
| arima_c = gr.Checkbox(True, label="ARIMA") | |
| prophet_c = gr.Checkbox(True, label="Prophet") | |
| lstm_c = gr.Checkbox(True, label="LSTM (Multivariate)") | |
| run_btn = gr.Button("Run Forecast") | |
| out_plot = gr.Plot() | |
| out_msg = gr.Textbox(label="Status / Notes") | |
| run_btn.click(fn=do_forecast, inputs=[horizon, arima_c, prophet_c, lstm_c], outputs=[out_plot, out_msg]) | |
| with gr.Tab("Backtest"): | |
| with gr.Row(): | |
| test_days = gr.Slider(30, 180, value=60, step=5, label="Backtest period (last N days)") | |
| with gr.Row(): | |
| arima_b = gr.Checkbox(True, label="ARIMA") | |
| prophet_b = gr.Checkbox(True, label="Prophet") | |
| lstm_b = gr.Checkbox(True, label="LSTM (Multivariate)") | |
| back_btn = gr.Button("Run Backtest") | |
| back_plot = gr.Plot() | |
| back_table = gr.Dataframe(headers=["Model", "RMSE", "MAPE"]) | |
| back_btn.click(fn=do_backtest, inputs=[test_days, arima_b, prophet_b, lstm_b], outputs=[back_plot, back_table]) | |
| demo.launch(share=True) |