Streamlit Dashboard: Prophet vs ARIMA Stock Forecasts

Build an interactive Streamlit app to load stock data, forecast with Prophet (auto-trend/seasonality) and ARIMA (order=5,1,0), compare via side-by-side MAE/RMSE/MAPE metrics, declare RMSE winner, and interpret MAPE (<10% good, <20% acceptable). Use caching to speed up yf.download, 80/20 train/test split.

Interactive Dashboard Setup Speeds Exploration

Start with st.set_page_config(layout="wide") and st.title("📊 Stock Forecast Dashboard") for a clean interface. Use sidebar controls for dynamic input: st.sidebar.date_input sets start_date (default 2020-01-01) and end_date (default 2021-01-01); st.sidebar.selectbox from a CSV-loaded ticker_list (e.g., index to "AA"); st.sidebar.slider("Forecast Days", 1, 60, 7) for n_day periods.

Cache data fetches with @st.cache_data def load_data(ticker): data = yf.download(ticker, start=start_date, end=end_date); data.reset_index(inplace=True) to avoid slow API repeats. Handle MultiIndex columns via if isinstance(data.columns, pd.MultiIndex): data.columns = data.columns.get_level_values(0). Guard against empty data or <10 rows with if data.empty or df.shape[0] < 10: st.stop().

Add KPI cards in columns: compute last_price = data'Close'.iloc-1, first_price = data'Close'.iloc0, change = last_price - first_price, pct_change = (change / first_price) * 100; display via col1.metric("Last Price", f"{last_price:.2f}"), etc. For raw data, use st.number_input("Rows", min_value=5, max_value=len(data), value=20) and st.dataframe(data.tail(int(show_last)), use_container_width=True) to inspect latest rows interactively.

Prep for models: df = data[['Date','Close']].copy(); df.columns = ['ds','y']; df.dropna() ensures Prophet format—missing 'ds'/'y' causes failures.

Prophet and ARIMA Deliver Complementary Forecasts

Prophet auto-detects trends and seasonality (weekly/yearly): prophet_model = Prophet(); prophet_model.fit(df); future = prophet_model.make_future_dataframe(periods=n_day); forecast_prophet = prophet_model.predict(future). Ideal for patterned time series without manual tuning.

ARIMA uses autoregression, differencing (d=1), moving averages (order=(5,1,0)): model = ARIMA(df['y'], order=(5,1,0)); model_fit = model.fit(). Suited for stable, consistent data needing statistical rigor—requires more data insight than Prophet.

Visualize in one Plotly go.Figure(): add actuals go.Scatter(x=df['ds'], y=df['y'], name='Actual'), overlay Prophet/ARIMA forecasts. Add toggles: st.selectbox("Select Model", ["All", "Prophet Only", "ARIMA Only"]), show_ci = st.checkbox("Show Confidence Interval"), highlight_forecast = st.checkbox("Highlight Forecast Area") for interactive exploration.

Metrics and Rules Pinpoint Better Model Per Stock

Split 80/20: split = int(len(df) * 0.8); train = df.iloc[:split]; test = df.iloc[split:]. Compute MAE = mean_absolute_error(test'y', pred), RMSE = sqrt(mean_squared_error(test'y', pred)), MAPE similarly.

Display side-by-side in columns: with col1: st.markdown("### Prophet"); st.metric("MAE", f"{mae_prophet:.4f}") etc. for both models. Pick winner by RMSE (penalizes large errors): if rmse_prophet < rmse_arima: winner = "Prophet". Show st.success(f"{winner} performs better based on RMSE").

Interpret MAPE: def interpret_mape(mape): if mape < 10: "✅ Good Model"; elif mape < 20: "⚠️ Acceptable Model"; else: "❌ Poor Model". Normalize error: avg_price = test'y'.mean(); relative_rmse = (best_rmse / avg_price) * 100 to contextualize against price scale.

Performance varies—Prophet better for "AA", ARIMA for "GOOGL" with smaller RMSE. No universal winner; evaluate per stock across metrics.

Deploy Fast: Streamlit Cloud Over Ngrok

Push to GitHub for Streamlit Cloud deployment—generates stable public link. For local testing, from pyngrok import ngrok; ngrok.connect(8501) provides temp URL, but unstable long-term. Full code at https://github.com/jihanKamilah/MarketPulse-Stock-Forecast-App.

Summarized by x-ai/grok-4.1-fast via openrouter

6934 input / 1754 output tokens in 14065ms

© 2026 Edge