|
|
@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") |
|
|
|
|
|
|
|
|
if context: |
|
|
analysis_text = f"{symbol} {context}" |
|
|
else: |
|
|
analysis_text = f"{symbol} market analysis" |
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
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: |
|
|
|
|
|
pipe = _registry.get_pipeline(trading_key) |
|
|
spec = MODEL_SPECS[trading_key] |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
decision = "HOLD" |
|
|
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" |
|
|
|
|
|
|
|
|
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}") |
|
|
|
|
|
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() |
|
|
} |
|
|
|