import gradio as gr import pandas as pd import yfinance as yf import plotly.graph_objects as go from plotly.subplots import make_subplots from statsforecast import StatsForecast from statsforecast.models import AutoARIMA def calculate_indicators(df): # RSI Calculation delta = df['Close'].diff() gain = (delta.where(delta > 0, 0)).rolling(window=14).mean() loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean() rs = gain / loss df['RSI'] = 100 - (100 / (1 + rs)) # MACD Calculation df['EMA12'] = df['Close'].ewm(span=12, adjust=False).mean() df['EMA26'] = df['Close'].ewm(span=26, adjust=False).mean() df['MACD'] = df['EMA12'] - df['EMA26'] df['Signal'] = df['MACD'].ewm(span=9, adjust=False).mean() return df def get_pro_chart(df, forecast_df, ticker): # Professional Subplots: Price + Indicators fig = make_subplots(rows=3, cols=1, shared_xaxes=True, vertical_spacing=0.05, row_heights=[0.5, 0.25, 0.25], subplot_titles=(f"{ticker} Forecast & Confidence Zone", "RSI Momentum", "MACD Trend")) # --- 1. Price + Forecast + CONFIDENCE ZONE --- # Shaded Confidence Area (AutoARIMA-hi-80 and AutoARIMA-lo-80) fig.add_trace(go.Scatter( x=forecast_df['ds'].tolist() + forecast_df['ds'].tolist()[::-1], y=forecast_df['AutoARIMA-hi-80'].tolist() + forecast_df['AutoARIMA-lo-80'].tolist()[::-1], fill='toself', fillcolor='rgba(0, 210, 255, 0.2)', line=dict(color='rgba(255,255,255,0)'), hoverinfo="skip", showlegend=True, name='80% Confidence' ), row=1, col=1) # Historical Price fig.add_trace(go.Scatter(x=df.index, y=df['Close'], name='Historical Price', line=dict(color='#00d2ff', width=2)), row=1, col=1) # AI Mean Forecast Line fig.add_trace(go.Scatter(x=forecast_df['ds'], y=forecast_df['AutoARIMA'], name='AI Target', line=dict(color='#F23645', dash='dot')), row=1, col=1) # --- 2. RSI --- fig.add_trace(go.Scatter(x=df.index, y=df['RSI'], name='RSI', line=dict(color='#FF9800')), row=2, col=1) fig.add_hline(y=70, line_dash="dot", line_color="red", row=2, col=1) fig.add_hline(y=30, line_dash="dot", line_color="green", row=2, col=1) # --- 3. MACD --- fig.add_trace(go.Bar(x=df.index, y=df['MACD'] - df['Signal'], name='Momentum', marker_color='#9d50bb'), row=3, col=1) # Styling to match your Aether UI fig.update_layout( template='plotly_dark', height=800, showlegend=True, paper_bgcolor='rgba(0,0,0,0)', # Transparent to show your terminal background plot_bgcolor='rgba(0,0,0,0)', font=dict(family="Lato", size=12) ) fig.update_xaxes(showgrid=False, zeroline=False) fig.update_yaxes(showgrid=False, zeroline=False) return fig def analyze(ticker, horizon): try: # Step 1: Data stock = yf.Ticker(ticker) df = stock.history(period="1y") if df.empty: return None, "Symbol Error" info = stock.info target_high = info.get('targetHighPrice', 'N/A') current_price = df['Close'].iloc[-1] # Step 2: Indicators df = calculate_indicators(df) # Step 3: StatsForecast with Levels data = df.reset_index()[['Date', 'Close']] data.columns = ['ds', 'y'] data['unique_id'] = ticker sf = StatsForecast(models=[AutoARIMA(season_length=5)], freq='B') sf.fit(data) # We request 80% level to get the 'lo-80' and 'hi-80' columns forecast = sf.predict(h=horizon, level=[80]) forecast = forecast.reset_index() # Step 4: Signal Analysis rsi_val = df['RSI'].iloc[-1] rsi_stat = "OVERSOLD" if rsi_val < 30 else "OVERBOUGHT" if rsi_val > 70 else "NEUTRAL" signal_html = f"""
CURRENT VALUE
RSI Analysis: {rsi_stat} ({rsi_val:.1f})
AI Forecast ({horizon}d): ${forecast['AutoARIMA'].iloc[-1]:.2f}
Confidence Zone: ${forecast['AutoARIMA-lo-80'].iloc[-1]:.2f} - ${forecast['AutoARIMA-hi-80'].iloc[-1]:.2f}
Error: {str(e)}
" # --- UI Layout --- with gr.Blocks(theme=gr.themes.Default(primary_hue="blue", secondary_hue="slate")) as demo: gr.Markdown("