Spaces:
Running
Running
File size: 3,754 Bytes
7efb01b 641c8d4 6c5a70b 7efb01b 6c5a70b 641c8d4 7efb01b 6c5a70b 641c8d4 7efb01b 6c5a70b 7efb01b 6c5a70b 7efb01b 6c5a70b 7efb01b 6c5a70b 7efb01b 6c5a70b 7efb01b 6c5a70b 7efb01b 6c5a70b 641c8d4 6c5a70b 7efb01b 6c5a70b 7efb01b 6c5a70b 7efb01b 6c5a70b 7efb01b 6c5a70b 7efb01b 6c5a70b 7efb01b 6c5a70b 7efb01b 6c5a70b 7efb01b 6c5a70b 7efb01b 6c5a70b 7efb01b 6c5a70b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | import os
import json
import logging
from typing import List, Optional
from fastapi import FastAPI, HTTPException, Header
from pydantic import BaseModel
from sentence_transformers import CrossEncoder
from huggingface_hub import InferenceClient
# --- LOGGING ---
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", datefmt="%H:%M:%S")
logger = logging.getLogger("Brain")
app = FastAPI()
# --- CONFIG ---
EXPECTED_TOKEN = os.getenv("SERVICE_SECRET", "default_secret")
HF_TOKEN = os.getenv("HF_TOKEN")
# --- MODELS ---
logger.info("⏳ Loading CrossEncoder...")
matcher_model = CrossEncoder("cross-encoder/stsb-distilroberta-base")
logger.info("✅ CrossEncoder Ready")
llm_client = InferenceClient(model="Qwen/Qwen2.5-72B-Instruct", token=HF_TOKEN)
# --- INPUT SCHEMAS ---
class MatchRequest(BaseModel):
target: str
candidates: List[str]
class MarketData(BaseModel):
rsi: float
macd: float
atr_pct: float
trend: str
price: float
bias: str
prob_up: float
class AnalyzeRequest(BaseModel):
question: str
context: str = "Crypto 15m Market"
data: Optional[MarketData] = None # ¡Ahora recibimos datos!
# --- ENDPOINTS ---
@app.get("/")
def health():
return {"status": "Brain V2 is Active 🧠", "matcher": "Loaded"}
@app.post("/match")
def match_market(req: MatchRequest, x_service_token: str = Header(None)):
if x_service_token != EXPECTED_TOKEN: raise HTTPException(401, "Unauthorized")
pairs = [[req.target, c] for c in req.candidates]
scores = matcher_model.predict(pairs)
best_idx = scores.argmax()
best_score = float(scores[best_idx])
if best_score > 0.4: # Bajamos umbral para captar más
return {"match": req.candidates[best_idx], "score": best_score}
return {"match": None, "score": best_score}
@app.post("/analyze")
def analyze_market(req: AnalyzeRequest, x_service_token: str = Header(None)):
if x_service_token != EXPECTED_TOKEN: raise HTTPException(401, "Unauthorized")
# Construimos un Prompt Rico con los datos técnicos
tech_summary = "No technical data provided."
if req.data:
tech_summary = (
f"STRATEGY SIGNAL: {req.data.bias} (Conf: {req.data.prob_up:.0%})\n"
f"INDICATORS: RSI={req.data.rsi:.1f} | MACD={req.data.macd:.4f} | Trend={req.data.trend}\n"
f"VOLATILITY (ATR): {req.data.atr_pct:.3f}% | Price: {req.data.price}"
)
logger.info(f"🤔 Analyzing: {req.question} | Bias: {req.data.bias if req.data else 'N/A'}")
prompt = f"""
You are a Senior Crypto Risk Officer for a high-frequency trading bot.
MARKET: "{req.question}"
TECHNICAL ANALYSIS:
{tech_summary}
TASK:
1. Check if the Market Title implies a "Trap" (e.g., weird rules, Elon Musk tweets, specific candle closes).
2. Validate if the Strategy Signal ({req.data.bias if req.data else 'N/A'}) makes sense given the indicators.
- Example: Buying BULLISH when RSI is > 80 is RISKY (Overbought).
- Example: Selling BEARISH when RSI is < 20 is RISKY (Oversold).
- Example: Trading when ATR is > 0.5% is RISKY (Too volatile).
OUTPUT JSON ONLY:
{{"verdict": "SAFE" or "TRAP", "reason": "concise explanation"}}
"""
try:
response = llm_client.chat_completion(
messages=[{"role": "user", "content": prompt}],
max_tokens=150, temperature=0.1
)
content = response.choices[0].message.content.replace("```json", "").replace("```", "").strip()
return json.loads(content)
except Exception as e:
logger.error(f"LLM Error: {e}")
return {"verdict": "SAFE", "reason": "LLM Failed, defaulting to Safe"} |