import gradio as gr import pandas as pd import yfinance as yf from utils import ( calculate_technical_indicators, generate_trading_signals, get_fundamental_data, predict_prices, create_price_chart, create_technical_chart, create_prediction_chart, ) import warnings warnings.filterwarnings("ignore") def analyze_stock(symbol, prediction_days=30): try: if not symbol.strip(): raise ValueError("Please enter a valid stock symbol.") if not symbol.endswith(".JK"): symbol = symbol.upper() + ".JK" stock = yf.Ticker(symbol) data = stock.history(period="6mo", interval="1d") if data.empty: raise ValueError("No price data available for this stock.") indicators = calculate_technical_indicators(data) signals = generate_trading_signals(data, indicators) fundamental_info = get_fundamental_data(stock) predictions = predict_prices(data, prediction_days=prediction_days) fig_price = create_price_chart(data, indicators) fig_technical = create_technical_chart(data, indicators) fig_prediction = create_prediction_chart(data, predictions) # kalkulasi TP1, TP2, SL last_price = data['Close'].iloc[-1] tp1 = last_price * (1 + (predictions.get("change_pct", 0) / 200)) tp2 = last_price * (1 + (predictions.get("change_pct", 0) / 100)) sl = last_price * 0.95 predictions["tp1"] = tp1 predictions["tp2"] = tp2 predictions["sl"] = sl return fundamental_info, indicators, signals, fig_price, fig_technical, fig_prediction, predictions except Exception as e: print(f"Error analyzing {symbol}: {e}") empty_fig = gr.Plot.update(value=None) empty_predictions = { "high_30d": 0, "low_30d": 0, "change_pct": 0, "summary": "Prediction unavailable.", } return {}, {}, {}, empty_fig, empty_fig, empty_fig, empty_predictions def update_analysis(symbol, prediction_days): ( fundamental_info, indicators, signals, fig_price, fig_technical, fig_prediction, predictions, ) = analyze_stock(symbol, prediction_days) if not fundamental_info: return ( "Unable to fetch stock data.", gr.Plot.update(value=None), gr.Plot.update(value=None), gr.Plot.update(value=None), ) fundamentals = f"""

COMPANY FUNDAMENTALS

Name: {fundamental_info.get('name', 'N/A')} ({symbol.upper()})
Current Price: Rp{fundamental_info.get('current_price', 0):,.2f}
Market Cap: {fundamental_info.get('market_cap', 0):,}
P/E Ratio: {fundamental_info.get('pe_ratio', 0):.2f}
Dividend Yield: {fundamental_info.get('dividend_yield', 0):.2f}%
Volume: {fundamental_info.get('volume', 0):,}
""" details_list = "".join( [f"
  • {line.strip()}
  • " for line in signals.get("details", "").split("\n") if line.strip()] ) trading_signal = f"""

    TECHNICAL SIGNAL SUMMARY

    Overall Trend: {signals.get('overall', 'N/A')}
    Signal Strength: {signals.get('strength', 0):.2f}%
    Support: Rp{signals.get('support', 0):,.2f}
    Resistance: Rp{signals.get('resistance', 0):,.2f}
    Stop Loss: Rp{signals.get('stop_loss', 0):,.2f}

    Detailed Signals: """ prediction = f"""

    30-DAY AI FORECAST (CHRONOS-BOLT)

    Predicted High: Rp{predictions.get('high_30d', 0):,.2f}
    Predicted Low: Rp{predictions.get('low_30d', 0):,.2f}
    Expected Change: {predictions.get('change_pct', 0):.2f}%

    TP1: Rp{predictions.get('tp1', 0):,.2f}
    TP2: Rp{predictions.get('tp2', 0):,.2f}
    Stop Loss: Rp{predictions.get('sl', 0):,.2f}

    Model Insight:
    {predictions.get('summary', 'No analysis available')} """ # Karena custom CSS dihapus, kita akan menggunakan div sederhana tanpa class 'panel-box' dan 'triple-panel' # Gradio secara otomatis akan menata elemen-elemen ini dengan lebih standar return ( f"""

    🏢 COMPANY FUNDAMENTALS

    {fundamentals.replace('

    COMPANY FUNDAMENTALS

    ', '').replace('
    ', '
    ')}

    📊 TECHNICAL SIGNAL SUMMARY

    {trading_signal.replace('

    TECHNICAL SIGNAL SUMMARY

    ', '').replace('
    ', '
    ').replace('
      ', '
        ')}

    🤖 AI FORECAST INSIGHTS

    {prediction.replace('

    30-DAY AI FORECAST (CHRONOS-BOLT)

    ', '').replace('
    ', '
    ')}
    """, fig_price, fig_technical, fig_prediction, ) # Theme management theme_state = gr.State(value="light") def toggle_theme(current_theme): return "dark" if current_theme == "light" else "light" def get_theme_css(theme): if theme == "dark": return """ """ else: return """ """ # --- Modern UI Redesign --- with gr.Blocks( title="REXPRO FINANCIAL AI DASHBOARD", css=""" .main-container { font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; max-width: 1400px; margin: 0 auto; padding: 20px; } .header-section { text-align: center; padding: 30px 0; margin-bottom: 30px; } .control-panel { background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); border-radius: 20px; padding: 25px; margin-bottom: 30px; box-shadow: 0 8px 32px rgba(0,0,0,0.1); } """, elem_classes=["main-container"] ) as app: # Header Section with gr.Row(elem_classes=["header-section"]): gr.HTML("""

    🚀 REXPRO FINANCIAL AI DASHBOARD

    Comprehensive stock analytics powered by AI forecasting and technical analysis

    """) # Theme toggle with gr.Row(): theme_toggle = gr.Button( "🌙 Switch to Dark Mode" if gr.load else "☀️ Switch to Light Mode", variant="primary", size="sm", elem_classes=["theme-toggle"] ) # Control Panel with gr.Row(elem_classes=["control-panel"]): with gr.Column(scale=2): symbol = gr.Textbox( label="📈 STOCK SYMBOL (IDX)", value="BBCA", placeholder="Example: BBCA, TLKM, ADRO, BMRI", interactive=True, elem_classes=["input-modern"] ) with gr.Column(scale=1): prediction_days = gr.Slider( label="🔮 FORECAST PERIOD (DAYS)", minimum=5, maximum=60, step=5, value=30, interactive=True, elem_classes=["input-modern"] ) with gr.Column(scale=1): analyze_button = gr.Button( "⚡ RUN ANALYSIS", variant="primary", size="lg", elem_classes=["theme-toggle"] ) # Modern theme-aware CSS theme_css = gr.HTML("") gr.HTML('
    ') # Enhanced report section with modern styling report_section = gr.HTML(elem_classes=["panel-modern"]) gr.HTML('
    ') with gr.Tab("📊 MARKET CHARTS", elem_classes=["tab-content"]): with gr.Row(): price_chart = gr.Plot(label="💹 PRICE & MOVING AVERAGES", elem_classes=["panel-card"]) technical_chart = gr.Plot(label="📈 TECHNICAL INDICATORS OVERVIEW", elem_classes=["panel-card"]) gr.HTML('
    ') prediction_chart = gr.Plot(label="🤖 AI FORECAST PROJECTION", elem_classes=["panel-card"]) analyze_button.click( fn=update_analysis, inputs=[symbol, prediction_days], outputs=[report_section, price_chart, technical_chart, prediction_chart], ) # Theme toggle functionality theme_toggle.click( fn=lambda x: toggle_theme(x), inputs=[theme_state], outputs=[theme_state] ) # Update theme CSS when theme changes theme_state.change( fn=get_theme_css, inputs=[theme_state], outputs=[theme_css] ) if __name__ == "__main__": app.launch(server_name="0.0.0.0", server_port=7860, ssr_mode=True)