import gradio as gr import numpy as np import tensorflow as tf from tensorflow import keras import yfinance as yf import pandas as pd from datetime import datetime, timedelta # Load the model model = keras.models.load_model('gru_model.h5') def prepare_data(df): """ Prepare stock data for the model - using percentage changes Expected features: Open, High, Low, Close, Volume """ # Select required columns features = ['Open', 'High', 'Low', 'Close', 'Volume'] data = df[features].copy() # Calculate percentage changes for prices (more stable normalization) price_cols = ['Open', 'High', 'Low', 'Close'] for col in price_cols: data[col] = data[col].pct_change().fillna(0) # Normalize volume using rolling window data['Volume'] = (data['Volume'] - data['Volume'].rolling(20, min_periods=1).mean()) / data['Volume'].rolling(20, min_periods=1).std() data['Volume'] = data['Volume'].fillna(0) # Clip extreme values to prevent outliers data = data.clip(-3, 3) return data.values, df['Close'].values def predict_stock_symbol(symbol, days_to_predict=10): """ Fetch stock data and make prediction with proper price conversion """ try: # Clean symbol symbol = symbol.strip().upper() # Fetch stock data (get more than 60 days to ensure we have enough) end_date = datetime.now() start_date = end_date - timedelta(days=120) # Get 120 days to be safe stock = yf.Ticker(symbol) df = stock.history(start=start_date, end=end_date) if df.empty: return f"āŒ Error: No data found for symbol '{symbol}'. Please check the symbol." if len(df) < 65: # Need extra days for percentage change calculation return f"āŒ Error: Not enough data. Found {len(df)} days, need at least 65 days." # Prepare data normalized_data, close_prices = prepare_data(df) # Get last 60 days for prediction last_60_days = normalized_data[-60:] # Reshape for model input (1, 60, 5) input_data = last_60_days.reshape(1, 60, 5) # Make prediction prediction = model.predict(input_data, verbose=0) predicted_values = prediction[0] # Get context for interpretation last_close = close_prices[-1] recent_closes = close_prices[-10:] # Last 10 closes for trend avg_recent_change = np.mean(np.diff(recent_closes) / recent_closes[:-1]) * 100 # Interpret predictions as potential future close prices # Assume predictions represent relative price movements interpreted_prices = [] current_price = last_close for i, pred_val in enumerate(predicted_values): # Convert normalized prediction to price change # Adjust this scaling based on your model's training price_change_pct = (pred_val - 1.0) * 5 # Scale factor - adjust as needed new_price = current_price * (1 + price_change_pct / 100) interpreted_prices.append(new_price) current_price = new_price # Use for next prediction # Format results result = f"šŸ“Š **Prediction for {symbol}**\n\n" result += f"šŸ“ˆ Current Close Price: **${last_close:.2f}**\n" result += f"šŸ“Š Recent Trend: {avg_recent_change:+.2f}% avg daily change\n" result += f"šŸ“… Data Period: {df.index[0].strftime('%Y-%m-%d')} to {df.index[-1].strftime('%Y-%m-%d')}\n" result += f"šŸ“Š Total Days Used: {len(df)}\n\n" result += f"šŸ”® **Price Predictions (Next 10 periods):**\n" for i, (raw_val, price) in enumerate(zip(predicted_values, interpreted_prices), 1): change_pct = ((price - last_close) / last_close) * 100 result += f" Day {i}: **${price:.2f}** ({change_pct:+.1f}%) [raw: {raw_val:.3f}]\n" # Summary statistics avg_predicted = np.mean(interpreted_prices) total_change = ((interpreted_prices[-1] - last_close) / last_close) * 100 result += f"\nšŸ“ˆ **Summary:**\n" result += f"• Average Predicted Price: **${avg_predicted:.2f}**\n" result += f"• Total 10-Day Change: **{total_change:+.1f}%**\n" result += f"• Prediction Range: ${min(interpreted_prices):.2f} - ${max(interpreted_prices):.2f}\n" result += f"\nāš ļø **Disclaimer:** Model predictions are experimental. Scaling factor may need adjustment based on training data." return result except Exception as e: return f"āŒ Error: {str(e)}" def get_stock_info(symbol): """ Get basic stock information """ try: symbol = symbol.strip().upper() stock = yf.Ticker(symbol) info = stock.info name = info.get('longName', 'N/A') sector = info.get('sector', 'N/A') country = info.get('country', 'N/A') return f"šŸ“‹ **Stock Info:**\n• Name: {name}\n• Sector: {sector}\n• Country: {country}" except: return "ā„¹ļø Stock info not available" # Create Gradio interface with gr.Blocks(title="Stock Symbol GRU Predictor", theme=gr.themes.Soft()) as demo: gr.Markdown("# šŸ“ˆ Stock Symbol GRU Predictor") gr.Markdown("Enter a stock symbol to fetch historical data and get GRU model predictions.") with gr.Row(): with gr.Column(scale=2): symbol_input = gr.Textbox( label="Stock Symbol", placeholder="e.g., AAPL, RNO.PA, TSLA, MSFT", value="RNO.PA" ) predict_btn = gr.Button("šŸ”® Predict", variant="primary", size="lg") info_btn = gr.Button("ā„¹ļø Get Stock Info", variant="secondary") with gr.Column(scale=3): output = gr.Textbox( label="Prediction Results", lines=15, max_lines=20 ) info_output = gr.Textbox( label="Stock Information", lines=4 ) # Event handlers predict_btn.click(predict_stock_symbol, inputs=symbol_input, outputs=output) info_btn.click(get_stock_info, inputs=symbol_input, outputs=info_output) # Example symbols gr.Markdown(""" ### šŸ“‹ Example Symbols: • **US Stocks**: AAPL, MSFT, GOOGL, TSLA, AMZN • **European**: RNO.PA (Renault), AIR.PA (Airbus), SAP (SAP SE) • **Indices**: ^GSPC (S&P 500), ^IXIC (NASDAQ) ### šŸ”§ How it works: 1. Fetches last 90 days of stock data 2. Uses last 60 days as model input 3. Normalizes data (prices relative to first close, volume min-max) 4. Generates 10 prediction values using your GRU model ### āš ļø Disclaimer: This is for educational purposes only. Not financial advice. """) if __name__ == "__main__": demo.launch()