from fastapi import FastAPI, HTTPException, Query from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel from typing import Dict, Any, List, Optional from data_processor import DataProcessor from sentiment_analyzer import SentimentAnalyzer from model_handler import ModelHandler from trading_logic import TradingLogic from plotter import create_mplfinance_chart data_processor = DataProcessor() sentiment_analyzer = SentimentAnalyzer() model_handler = ModelHandler() trading_logic = TradingLogic() app = FastAPI( title="Ultimate Market Analysis & Prediction API", version="1.0.0", description="API for fetching market data, technical indicators, Chronos-2 predictions, and simulated analysis for GC=F and BTC-USD." ) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) class TradingMetrics(BaseModel): Ticker: str Current_Price: str Signal: str Confidence: str Take_Profit: str Stop_Loss: str RSI: str MACD: str Volume: str class ChartAnalysisResponse(BaseModel): chart_html_base64: Optional[str] = None metrics: Optional[TradingMetrics] = None raw_predictions: Optional[List[float]] = None error: Optional[str] = None class SentimentAnalysisResponse(BaseModel): sentiment_score: float news_summary_html: str class FundamentalsResponse(BaseModel): fundamentals_data: Dict[str, Any] @app.get("/") def read_root(): return {"message": "Welcome to the Ultimate Market Analysis API. Use /docs for API documentation."} @app.get("/analysis/chart", response_model=ChartAnalysisResponse) def get_chart_analysis( ticker: str = Query(..., description="Market Ticker (e.g., GC=F, BTC-USD)"), interval: str = Query(..., description="Time Interval (e.g., 1d, 1h, 5m)") ): try: df = data_processor.get_market_data(ticker, interval) if df.empty: return ChartAnalysisResponse(error=f"No data available for {ticker} at {interval}") df = data_processor.calculate_indicators(df) prepared_data = data_processor.prepare_for_chronos(df) predictions = model_handler.predict(prepared_data, horizon=10) current_price = df['Close'].iloc[-1] chart_html = create_mplfinance_chart( df, ticker=f'{ticker} ({interval})', predictions=predictions ) signal, confidence = trading_logic.generate_signal( predictions, current_price, df ) # Pastikan ATR tersedia sebelum menghitung TP/SL atr_value = df['ATR'].iloc[-1] if 'ATR' in df.columns and not df['ATR'].empty else 0 tp, sl = trading_logic.calculate_tp_sl( current_price, atr_value, signal ) metrics = TradingMetrics( Ticker=ticker, Current_Price=f"${current_price:.2f}", Signal=signal.upper(), Confidence=f"{confidence:.1%}", Take_Profit=f"${tp:.2f}" if tp else "N/A", Stop_Loss=f"${sl:.2f}" if sl else "N/A", RSI=f"{df['RSI'].iloc[-1]:.1f}", MACD=f"{df['MACD'].iloc[-1]:.4f}", Volume=f"{df['Volume'].iloc[-1]:,.0f}" ) return ChartAnalysisResponse( chart_html_base64=chart_html, metrics=metrics, raw_predictions=predictions.tolist() ) except Exception as e: # PENTING: Menggunakan HTTPException 500 dengan detail spesifik raise HTTPException(status_code=500, detail=f"Error in chart analysis: {str(e)}") @app.get("/analysis/sentiment", response_model=SentimentAnalysisResponse) def get_sentiment_analysis(ticker: str = Query(..., description="Market Ticker (e.g., GC=F, BTC-USD)")): try: sentiment_score, news_summary_html = sentiment_analyzer.analyze_market_sentiment(ticker) return SentimentAnalysisResponse( sentiment_score=sentiment_score, news_summary_html=news_summary_html ) except Exception as e: raise HTTPException(status_code=500, detail=f"Error in sentiment analysis: {str(e)}") @app.get("/analysis/fundamentals", response_model=FundamentalsResponse) def get_fundamentals_analysis(ticker: str = Query(..., description="Market Ticker (e.g., GC=F, BTC-USD)")): try: fundamentals = data_processor.get_fundamental_data(ticker) return FundamentalsResponse(fundamentals_data=fundamentals) except Exception as e: raise HTTPException(status_code=500, detail=f"Error in fundamentals analysis: {str(e)}")