@app.post("/api/trading/decision") async def trading_decision(request: Dict[str, Any]): """ FIXED: Get trading decision based on sentiment classification. Uses ElKulako/cryptobert (classification model) instead of generation. Logic: - BULLISH/POSITIVE label -> BUY - BEARISH/NEGATIVE label -> SELL - NEUTRAL or error -> HOLD Always returns valid JSON (never crashes with 500). """ try: symbol = request.get("symbol", "").strip().upper() context = request.get("context", "").strip() if not symbol: raise HTTPException(status_code=400, detail="Symbol is required") # Build analysis text if context: analysis_text = f"{symbol} {context}" else: analysis_text = f"{symbol} market analysis" # Default safe response default_response = { "success": True, "available": False, "decision": "HOLD", "confidence": 0.5, "rationale": "Unable to analyze sentiment - defaulting to HOLD for safety", "symbol": symbol, "model": "fallback", "context_provided": bool(context), "timestamp": datetime.now().isoformat() } try: from ai_models import _registry, MODEL_SPECS, ModelNotAvailable # Try to use the trading model (crypto_trading_lm -> ElKulako/cryptobert) trading_key = "crypto_trading_lm" if trading_key not in MODEL_SPECS: logger.warning("Trading model key not found in MODEL_SPECS") return default_response try: # Get the classification pipeline (lazy loaded) pipe = _registry.get_pipeline(trading_key) spec = MODEL_SPECS[trading_key] # Run classification result = pipe(analysis_text[:512]) if isinstance(result, list) and result: result = result[0] label = result.get("label", "NEUTRAL").upper() score = result.get("score", 0.5) # FIXED LOGIC: Map label to trading decision decision = "HOLD" # Default if "BULLISH" in label or "POSITIVE" in label or "LABEL_2" in label: decision = "BUY" elif "BEARISH" in label or "NEGATIVE" in label or "LABEL_0" in label: decision = "SELL" else: decision = "HOLD" # Build rationale sentiment_word = "bullish" if decision == "BUY" else ("bearish" if decision == "SELL" else "neutral") rationale = f"Model detected {sentiment_word} sentiment (label: {label}, confidence: {score:.2f})" if context: rationale += f" based on: {context[:200]}" return { "success": True, "available": True, "decision": decision, "confidence": float(score), "rationale": rationale, "symbol": symbol, "model": spec.model_id, "sentiment": sentiment_word, "raw_label": label, "context_provided": bool(context), "timestamp": datetime.now().isoformat() } except ModelNotAvailable as e: logger.warning(f"Trading model not available: {e}") default_response["error"] = f"Model unavailable: {str(e)[:100]}" default_response["note"] = "Model in cooldown or failed to load" return default_response except ImportError: logger.error("ai_models module not available") default_response["error"] = "AI models module not available" return default_response except Exception as e: logger.warning(f"Sentiment analysis failed: {e}") default_response["error"] = f"Analysis failed: {str(e)[:100]}" default_response["note"] = "Using default HOLD signal due to analysis failure" return default_response except HTTPException: raise except Exception as e: logger.error(f"Trading decision endpoint error: {e}") # Never crash - always return valid JSON return { "success": True, "available": False, "error": f"Endpoint error: {str(e)[:100]}", "decision": "HOLD", "confidence": 0.5, "rationale": "Error occurred during analysis - defaulting to HOLD for safety", "symbol": request.get("symbol", "UNKNOWN"), "timestamp": datetime.now().isoformat() }