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, calculate_advanced_risk_metrics ) import warnings import numpy as np 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="1y", 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) risk_metrics = calculate_advanced_risk_metrics(data.copy()) 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 yang diperbarui berdasarkan quantiles/range prediksi last_price = data['Close'].iloc[-1] # Dapatkan array prediksi dengan fallback ke array yang berisi harga terakhir q05 = predictions.get('values', np.array([last_price])) q01 = predictions.get('q01', np.array([last_price * 0.95])) q09 = predictions.get('q09', np.array([last_price * 1.05])) # Robust max/min calculation q05_max = np.max(q05) if q05.size > 0 else last_price q09_max = np.max(q09) if q09.size > 0 else last_price * 1.05 q01_min = np.min(q01) if q01.size > 0 else last_price * 0.95 # Target/SL calculation tp1 = (last_price + q05_max) / 2 tp2 = q09_max sl = q01_min if tp1 > tp2: tp1, tp2 = tp2, tp1 if sl > last_price: sl = last_price * 0.95 # Fallback predictions["tp1"] = tp1 predictions["tp2"] = tp2 predictions["sl"] = sl return fundamental_info, indicators, signals, risk_metrics, fig_price, fig_technical, fig_prediction, predictions except Exception as e: print(f"Error analyzing {symbol}: {e}") try: stock = yf.Ticker(symbol) data = stock.history(period="1d", interval="1d") last_price = data['Close'].iloc[-1] if not data.empty else 0 except: last_price = 0 default_tp1 = last_price * 1.01 default_tp2 = last_price * 1.02 default_sl = last_price * 0.95 empty_predictions = { "high_30d": 0, "low_30d": 0, "change_pct": 0, "summary": f"Prediction unavailable. Model error: {e}", # Menggunakan list kosong sebagai fallback jika error terjadi "q01": [], "q09": [], "tp1": default_tp1, "tp2": default_tp2, "sl": default_sl, } empty_risk = {"error": "Prediction Model Failed to Load/Run. See console for details."} # Mengembalikan None untuk output plot Gradio untuk membersihkan plot return {}, {}, {}, empty_risk, None, None, None, empty_predictions def update_analysis(symbol, prediction_days): ( fundamental_info, indicators, signals, risk_metrics, fig_price, fig_technical, fig_prediction, predictions, ) = analyze_stock(symbol, prediction_days) # Cek apakah ada plot yang None (berarti ada error) if fig_price is None: error_msg = f"Unable to run AI prediction or fetch data for {symbol.upper()}. Check the model logs for details." tp_sl_info = f"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', 'Data fetching or model execution failed. Cannot proceed with analysis.')}" return ( f"""
{error_msg}

{tp_sl_info}""", None, # fig_price None, # fig_technical None, # fig_prediction ) # --- AI FORECAST --- q01_values = predictions.get('q01', []) q09_values = predictions.get('q09', []) # FIX: Robust check untuk array NumPy/list kosong # Konversi ke NumPy array dan periksa ukuran (.size > 0) untuk menghindari ValueError q01_arr = np.array(q01_values) q09_arr = np.array(q09_values) band_min = float(np.min(q01_arr)) if q01_arr.size > 0 else 0 band_max = float(np.max(q09_arr)) if q09_arr.size > 0 else 0 # --- FUNDAMENTALS --- 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):,}
""" # --- TECHNICAL SIGNAL --- 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: """ # --- RISK METRICS --- risk_details = "" if "error" in risk_metrics: risk_details = f"{risk_metrics['error']}" else: for key, value in risk_metrics.items(): risk_details += f"{key.replace('_', ' ')}: {value}
    " risk_report = f"""

    ADVANCED RISK METRICS (1Y HISTORICAL)

    {risk_details} """ # --- AI FORECAST (Lanjutan) --- prediction = f"""

    {prediction_days}-DAY AI FORECAST (CHRONOS-2 + COVARIATES)

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

    RISK ANALYSIS (90% CONFIDENCE)

    Min. Possible Price (Q0.1): Rp{band_min:,.2f}
    Max. Possible Price (Q0.9): Rp{band_max:,.2f}
    --- TP1 (Conservative Target): Rp{predictions.get('tp1', 0):,.2f}
    TP2 (Aggressive Target): Rp{predictions.get('tp2', 0):,.2f}
    Stop Loss (Q0.1 based): Rp{predictions.get('sl', 0):,.2f}

    Model Insight:
    {predictions.get('summary', 'No analysis available')} """ # Menggunakan tata letak 4 kolom/panel return ( f"""
    {fundamentals}
    {trading_signal}
    {risk_report}
    {prediction}
    """, fig_price, fig_technical, fig_prediction, ) # --- Gradio Interface --- with gr.Blocks( title="REXPRO FINANCIAL AI DASHBOARD" ) as app: gr.Markdown("# REXPRO FINANCIAL AI DASHBOARD") gr.Markdown( "Comprehensive stock analytics powered by **AI forecasting, advanced risk metrics, and future technical analysis.**" ) with gr.Row(): symbol = gr.Textbox( label="STOCK SYMBOL (IDX)", value="BBCA", placeholder="Example: BBCA, TLKM, ADRO, BMRI", interactive=True, ) prediction_days = gr.Slider( label="FORECAST PERIOD (DAYS)", minimum=5, maximum=60, step=5, value=30, interactive=True, ) analyze_button = gr.Button("RUN ADVANCED ANALYSIS") gr.Markdown("---") report_section = gr.HTML() gr.Markdown("---") with gr.Tab("MARKET CHARTS"): with gr.Row(): price_chart = gr.Plot(label="PRICE & MOVING AVERAGES") technical_chart = gr.Plot(label="TECHNICAL INDICATORS OVERVIEW") gr.Markdown("---") prediction_chart = gr.Plot(label="AI FORECAST & FUTURE TECHNICAL PROJECTION") analyze_button.click( fn=update_analysis, inputs=[symbol, prediction_days], outputs=[report_section, price_chart, technical_chart, prediction_chart], ) if __name__ == "__main__": app.launch(server_name="0.0.0.0", server_port=7860, ssr_mode=True)