Spaces:
Running
Running
| 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) | |
| # Fetching data for a slightly longer period for better analysis base | |
| 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) | |
| 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": f"Error: {e}", | |
| "tp1": 0, | |
| "tp2": 0, | |
| "sl": 0, | |
| } | |
| # Mengembalikan string kosong untuk data yang akan ditampilkan di Markdown | |
| 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 and "Error" in predictions.get("summary", ""): | |
| # Jika terjadi error, kembalikan hasil error ke semua output | |
| error_msg = f"**ERROR:** {predictions.get('summary')}" | |
| return ( | |
| gr.Markdown.update(value=error_msg), | |
| gr.Markdown.update(value=error_msg), | |
| gr.Markdown.update(value=error_msg), | |
| gr.Plot.update(value=None), | |
| gr.Plot.update(value=None), | |
| gr.Plot.update(value=None), | |
| ) | |
| # --- Formatting Fundamental Data --- | |
| fundamentals_text = 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):,} | |
| """ | |
| # --- Formatting Technical Signals --- | |
| details_list = "".join( | |
| [f"<li>{line.strip()}</li>" for line in signals.get("details", "").split("\n") if line.strip()] | |
| ) | |
| trading_signal_text = 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: | |
| <ul style="margin-top: 8px; padding-left: 20px; line-height: 1.6;"> | |
| {details_list} | |
| </ul> | |
| """ | |
| # --- Formatting Prediction Data --- | |
| prediction_text = f""" | |
| ### 30-DAY AI FORECAST | |
| - **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}%** | |
| #### Trading Targets | |
| - **TP1 (Target Price 1):** Rp{predictions.get('tp1', 0):,.2f} | |
| - **TP2 (Target Price 2):** Rp{predictions.get('tp2', 0):,.2f} | |
| - **Stop Loss:** Rp{predictions.get('sl', 0):,.2f} | |
| #### Model Insight | |
| > {predictions.get('summary', 'No analysis available')} | |
| """ | |
| return ( | |
| gr.Markdown.update(value=fundamentals_text), | |
| gr.Markdown.update(value=trading_signal_text), | |
| gr.Markdown.update(value=prediction_text), | |
| fig_price, | |
| fig_technical, | |
| fig_prediction, | |
| ) | |
| # --- STRUKTUR UI BARU --- | |
| with gr.Blocks( | |
| title="REXPRO FINANCIAL AI DASHBOARD", | |
| # Menggunakan tema standar Gradio. Perbaikan layout yang membuat professional. | |
| ) as app: | |
| gr.Markdown("# REXPRO FINANCIAL AI DASHBOARD") | |
| gr.Markdown( | |
| "Aplikasi Analisis Saham Komprehensif IDX, didukung oleh **Peramalan AI (CHRONOS-BOLT)** dan Analisis Teknikal." | |
| ) | |
| # 1. Row Kontrol Utama | |
| with gr.Row(variant="panel"): # Menggunakan panel untuk membedakan area kontrol | |
| with gr.Column(scale=1): | |
| symbol = gr.Textbox( | |
| label="MASUKKAN SIMBOL SAHAM (IDX)", | |
| value="BBCA", | |
| placeholder="Contoh: BBCA, TLKM, ADRO, BMRI", | |
| interactive=True, | |
| show_label=True | |
| ) | |
| with gr.Column(scale=1): | |
| prediction_days = gr.Slider( | |
| label="PERIODE PERAMALAN (HARI)", | |
| minimum=5, | |
| maximum=60, | |
| step=5, | |
| value=30, | |
| interactive=True, | |
| show_label=True | |
| ) | |
| with gr.Column(min_width=150, scale=0): | |
| # Tombol diletakkan di bawah input untuk tampilan yang lebih rapi | |
| analyze_button = gr.Button("RUN ANALYSIS", size="lg") | |
| # 2. Tab untuk Hasil | |
| with gr.Tabs(): | |
| # Tab 1: Ringkasan Analisis (Textual) | |
| with gr.TabItem("RINGKASAN & TARGET"): | |
| # Menggunakan gr.Row untuk membuat 3 "kartu" atau kolom hasil | |
| with gr.Row(): | |
| # Grup untuk Fundamental | |
| with gr.Group(): | |
| fundamentals_output = gr.Markdown( | |
| "Hasil Fundamental akan muncul di sini.", | |
| elem_classes=["card"] | |
| ) | |
| # Grup untuk Sinyal Teknikal | |
| with gr.Group(): | |
| trading_signal_output = gr.Markdown( | |
| "Ringkasan Sinyal Teknikal akan muncul di sini.", | |
| elem_classes=["card"] | |
| ) | |
| # Grup untuk Prediksi AI & Target | |
| with gr.Group(): | |
| prediction_output = gr.Markdown( | |
| "Hasil Peramalan AI & Target Trading akan muncul di sini.", | |
| elem_classes=["card"] | |
| ) | |
| # Tab 2: Grafik Analisis | |
| with gr.TabItem("VISUALISASI GRAFIK"): | |
| gr.Markdown("## Grafik Analisis Saham") | |
| with gr.Row(): | |
| # Grafik 1: Harga & MA | |
| with gr.Column(scale=1): | |
| price_chart = gr.Plot(label="1. HARGA & RATA-RATA BERGERAK (MA)", height=450) | |
| # Grafik 2: Indikator Teknikal | |
| with gr.Column(scale=1): | |
| technical_chart = gr.Plot(label="2. INDIKATOR TEKNIKAL UTAMA (RSI/MACD)", height=450) | |
| gr.Markdown("---") | |
| with gr.Row(): | |
| # Grafik 3: Prediksi AI | |
| prediction_chart = gr.Plot(label="3. PROYEKSI PERAMALAN AI", height=500) | |
| # Definisi Event Handler | |
| analyze_button.click( | |
| fn=update_analysis, | |
| inputs=[symbol, prediction_days], | |
| outputs=[ | |
| fundamentals_output, | |
| trading_signal_output, | |
| prediction_output, | |
| price_chart, | |
| technical_chart, | |
| prediction_chart, | |
| ], | |
| ) | |
| if __name__ == "__main__": | |
| app.launch(server_name="0.0.0.0", server_port=7860, ssr_mode=True) |