import streamlit as st import pandas as pd import plotly.express as px from datetime import datetime, timedelta import requests from models.prophet_model import ProphetModel from models.arima_model import ArimaModel from models.lstm_model import LSTMModel from utils.preprocessing import preprocess_data from utils.visualization import plot_decomposition, plot_forecast # st.set_page_config(page_title="Crypto Forecasting Platform", layout="wide") class CryptoForecastingApp: def __init__(self): self.available_coins = { 'Bitcoin': {'binance': 'BTCUSDT', 'yahoo': 'BTC-USD'}, 'Ethereum': {'binance': 'ETHUSDT', 'yahoo': 'ETH-USD'}, 'Dogecoin': {'binance': 'DOGEUSDT', 'yahoo': 'DOGE-USD'}, 'Cardano': {'binance': 'ADAUSDT', 'yahoo': 'ADA-USD'}, 'Solana': {'binance': 'SOLUSDT', 'yahoo': 'SOL-USD'} } self.available_models = ['Prophet', 'ARIMA', 'LSTM'] def get_binance_data(self, symbol, start_date, end_date): try: # Convert dates to timestamps (milliseconds) start_ts = int(datetime.combine(start_date, datetime.min.time()).timestamp() * 1000) end_ts = int(datetime.combine(end_date, datetime.max.time()).timestamp() * 1000) # Binance API endpoint for klines (candlestick) data url = "https://api.binance.com/api/v3/klines" params = { "symbol": symbol, "interval": "1d", # Daily data "startTime": start_ts, "endTime": end_ts, "limit": 1000 } response = requests.get(url, params=params) if response.status_code != 200: return None data = response.json() if not data: return None # Convert to DataFrame df = pd.DataFrame(data, columns=[ 'timestamp', 'Open', 'High', 'Low', 'Close', 'Volume', 'close_time', 'quote_volume', 'trades', 'taker_base', 'taker_quote', 'ignored' ]) # Convert timestamp to datetime df['Date'] = pd.to_datetime(df['timestamp'], unit='ms') df.set_index('Date', inplace=True) # Convert string values to float for col in ['Open', 'High', 'Low', 'Close', 'Volume']: df[col] = df[col].astype(float) return df[['Open', 'High', 'Low', 'Close', 'Volume']] except Exception as e: st.error(f"Error fetching from Binance: {str(e)}") return None def get_yahoo_data(self, symbol, start_date, end_date): try: import yfinance as yf data = yf.download(symbol, start=start_date, end=end_date) return data if not data.empty else None except Exception as e: st.error(f"Error fetching from Yahoo: {str(e)}") return None def get_crypto_data(self, coin_info, start_date, end_date): # Try Binance first data = self.get_binance_data(coin_info['binance'], start_date, end_date) # If Binance fails, try Yahoo Finance if data is None: st.warning("Binance data unavailable, trying Yahoo Finance...") data = self.get_yahoo_data(coin_info['yahoo'], start_date, end_date) if data is None: st.error("Unable to fetch data from both Binance and Yahoo Finance") return None return data def run(self): st.title("Cryptocurrency Price Forecasting & Analysis Platform") # Sidebar controls st.sidebar.header("Controls") selected_coin_name = st.sidebar.selectbox('Choose Cryptocurrency', list(self.available_coins.keys())) selected_coin = self.available_coins[selected_coin_name] # Date range selection col1, col2 = st.sidebar.columns(2) with col1: start_date = st.date_input( "Start Date", datetime.now().date() - timedelta(days=365), min_value=datetime(2015, 1, 1).date(), max_value=datetime.now().date() ) with col2: end_date = st.date_input( "End Date", datetime.now().date(), min_value=start_date, max_value=datetime.now().date() ) if start_date >= end_date: st.error("Error: End date must be after start date.") return selected_model = st.sidebar.selectbox('Choose Model', self.available_models) forecast_days = st.sidebar.slider('Forecast Days', 7, 90, 30) if st.sidebar.button('Generate Forecast'): with st.spinner('Loading historical data...'): data = self.get_crypto_data(selected_coin, start_date, end_date) if data is not None and not data.empty: try: with st.spinner('Processing data...'): processed_data = preprocess_data(data) # Display basic stats st.subheader("Current Statistics") metrics_col1, metrics_col2, metrics_col3 = st.columns(3) with metrics_col1: current_price = float(data['Close'].iloc[-1]) st.metric("Current Price", f"${current_price:.2f}") with metrics_col2: if len(data) > 1: price_change = float(data['Close'].iloc[-1] - data['Close'].iloc[-2]) price_change_pct = (price_change / float(data['Close'].iloc[-2])) * 100 st.metric("24h Change", f"${price_change:.2f}", f"{price_change_pct:.2f}%") else: st.metric("24h Change", "N/A") with metrics_col3: current_volume = float(data['Volume'].iloc[-1]) st.metric("24h Volume", f"${current_volume:,.0f}") # Show price history st.subheader("Price History") fig = px.line(data, y='Close', title=f'{selected_coin_name} Price History') st.plotly_chart(fig) # Show decomposition st.subheader("Time Series Decomposition") with st.spinner('Generating decomposition plot...'): try: decomp_fig = plot_decomposition(processed_data) st.plotly_chart(decomp_fig) except Exception as e: st.error(f"Error in decomposition: {str(e)}") # Generate and show forecast st.subheader(f"Price Forecast ({forecast_days} days)") with st.spinner(f'Training {selected_model} model and generating forecast...'): try: if selected_model == 'Prophet': model = ProphetModel() elif selected_model == 'ARIMA': model = ArimaModel() else: model = LSTMModel() forecast = model.train_and_predict(processed_data, forecast_days) forecast_fig = plot_forecast(processed_data, forecast, selected_model) st.plotly_chart(forecast_fig) # Show model performance metrics st.subheader("Model Performance Metrics") metrics = model.get_metrics() metrics_df = pd.DataFrame(metrics, index=[0]) st.table(metrics_df) except Exception as e: st.error(f"Error in forecasting: {str(e)}") except Exception as e: st.error(f"Error processing data: {str(e)}") else: st.warning("No data available for the selected date range. Please try a different range.") # Add information about data source st.sidebar.markdown("---") st.sidebar.info("Data sources: Binance API (primary) and Yahoo Finance (backup)") with st.expander("How to use this app"): st.write(""" 1. Select a cryptocurrency from the dropdown menu 2. Choose your desired date range (data available from 2015 onwards) 3. Select a forecasting model: - Prophet: Good for general trends - ARIMA: Good for short-term forecasts - LSTM: Good for complex patterns 4. Choose the number of days to forecast 5. Click 'Generate Forecast' to see the results Note: The app uses Binance API as the primary data source and falls back to Yahoo Finance if needed. """) if __name__ == "__main__": app = CryptoForecastingApp() app.run()