""" BrainSteel Fin — Decio Agent (v2) Chief Strategist & Risk Officer 5-Pillar decision engine: Confiança < 70% → HOLD. BUY/SELL → SL 2% / TP 5%. """ import os, json, requests from datetime import datetime class DecioAgent: def __init__(self, brief_data): self.name = "Decio" self.brief_data = brief_data self.openrouter_key = os.getenv("DECIO_API_KEY", os.getenv("OPENROUTER_API_KEY", "")) self.openrouter_url = "https://openrouter.ai/api/v1/chat/completions" self.model = "deepseek/deepseek-v4-flash:free" # ── CORE RULE: confidence < 70% → always HOLD ───────────────────────── def _risk_filter(self, confidence, signal): """Décio is a mathematician: no confidence ≥70%, no operation.""" return confidence < 70 or signal not in ("ALTA", "BAIXA") # ── Stop Loss / Take Profit ──────────────────────────────────────────────── def _calc_levels(self, price, action): if action == "BUY": return round(price * 0.98, 2), round(price * 1.05, 2) elif action == "SELL": return round(price * 1.02, 2), round(price * 0.95, 2) return 0, 0 # ── LLM Decision (with 5-pillar context) ───────────────────────────────── def _decide_llm(self): if not self.openrouter_key: return None briefing = self.brief_data.get("briefing_for_decio", self.brief_data.get("summary", "")) pillars = self.brief_data.get("_all_pillars", {}) signal = self.brief_data.get("signal", "NEUTRO") confidence = self.brief_data.get("confidence", 50) price = self.brief_data.get("price", 0) # Build rich context for LLM tech = pillars.get("Technical", {}) deriv = pillars.get("Derivatives", {}) liq = pillars.get("Liquidations", {}) fgi = pillars.get("FearGreed", {}) sent = pillars.get("Sentiment", {}) dom = pillars.get("Dominance", {}) context = f"""Você é o Décio, estrategista soberano da BrainSteel Fin. Analítico, frio, matemático. Preservação de capital > lucro rápido. BRIEFING DO BRIEF: {briefing} PILARES DE ANÁLISE DO BRIEF: Technical: RSI={tech.get('rsi','?')} | MACD hist={tech.get('macd_histogram','?')} | Var 24h={tech.get('change_24h','?')}% Derivatives: Funding={deriv.get('funding_rate_pct','?')}%. Mark={deriv.get('mark_price','?')} Liquidations: Long Ratio={liq.get('long_ratio_pct','?')}%. Sentiment={liq.get('sentiment','?')} Fear&Greed: Index={fgi.get('fgi_value','?')} ({fgi.get('fgi_class','?')}) Sentiment: {sent.get('sentiment_label','?')} BTC Dominance: {dom.get('btc_dominance_pct','?')}% Bullish signals: {self.brief_data.get('bullish_signals',0)} | Bearish: {self.brief_data.get('bearish_signals',0)} REGRAS OPERACIONAIS: Confiança < 70% → decisão SEMPRE HOLD (nunca opera) Confiança ≥ 70% + sinal=ALTA → BUY Confiança ≥ 70% + sinal=BAIXA → SELL BUY/SELL → Stop Loss = preço × 0.98 (-2%) | Take Profit = preço × 1.05 (+5%) HOLD → stop_loss=0, take_profit=0 Responda EXCLUSIVAMENTE com JSON válido (sem texto fora do JSON): {{"decision": "BUY|SELL|HOLD", "confidence": N, "justification": "frase curta", "stop_loss": N, "take_profit": N}}""" try: headers = { "Authorization": f"Bearer {self.openrouter_key}", "Content-Type": "application/json", "HTTP-Referer": "https://brainsteel.fin", "X-Title": "BrainSteel Fin" } payload = { "model": self.model, "messages": [{"role": "user", "content": context}], "max_tokens": 400 } r = requests.post(self.openrouter_url, json=payload, headers=headers, timeout=30) if r.status_code == 200: resp_data = r.json() content = resp_data["choices"][0]["message"]["content"].strip() content = content.strip("` \n") if content.startswith("json"): content = content[4:] data = json.loads(content) # Capture token usage usage = resp_data.get("usage", {}) if usage: try: from agents.token_tracker import log_tokens log_tokens("Decio", self.model, usage) except: pass # Apply risk filter if self._risk_filter(data.get("confidence", 0), signal): data["decision"] = "HOLD" data["justification"] = "Confiança < 70% — preservando capital" data["stop_loss"] = 0 data["take_profit"] = 0 return data except: pass return None # ── Local decision (fallback) ──────────────────────────────────────────── def _local_decision(self): confidence = self.brief_data.get("confidence", 50) signal = self.brief_data.get("signal", "NEUTRO") price = self.brief_data.get("price", 0) net = self.brief_data.get("net_signal", 0) rsi = self.brief_data.get("rsi", 50) # Overbought/oversold confirmation if signal == "ALTA" and rsi > 75: # Extremely overbought — skip BUY even if signal says ALTA decision = "HOLD" sl, tp = 0, 0 justification = "ALTA sinal cancelada — RSI overbought (75+)" elif signal == "BAIXA" and rsi < 25: decision = "HOLD" sl, tp = 0, 0 justification = "BAIXA sinal cancelada — RSI oversold (25-)" elif self._risk_filter(confidence, signal): decision = "HOLD" sl, tp = 0, 0 justification = f"Confiança {confidence}% < 70% — preservando capital" elif signal == "ALTA": decision = "BUY" sl, tp = self._calc_levels(price, "BUY") justification = f"Sinal ALTA conf {confidence}%, RSI {rsi}, net {net:+d}" elif signal == "BAIXA": decision = "SELL" sl, tp = self._calc_levels(price, "SELL") justification = f"Sinal BAIXA conf {confidence}%, RSI {rsi}, net {net:+d}" else: decision = "HOLD" sl, tp = 0, 0 justification = "Sinal neutro — aguardando clareza" return { "decision": decision, "confidence": confidence, "justification": justification, "stop_loss": sl, "take_profit": tp, "signal": signal } # ── Dynamic Allocation (Kelly Scale) ───────────────────────────────────── def _calc_allocation(self, confidence, decision): """Calcula a alocação de banca dinâmica baseada na confiança do sinal.""" if decision not in ("BUY", "SELL") or confidence < 70: return 0 if confidence >= 90: return 35 # Mão forte (alta convicção) elif confidence >= 80: return 25 # Mão padrão else: return 15 # Mão leve (cautela, 70-79%) # ── Main run ─────────────────────────────────────────────────────────── def run(self): llm_result = self._decide_llm() result = llm_result if llm_result else self._local_decision() price = self.brief_data.get("price", 0) conf = result.get("confidence", 50) action = result["decision"] amount_pct = self._calc_allocation(conf, action) output = { "action": action, "amount_pct": amount_pct, "stop_loss": result.get("stop_loss", 0), "take_profit": result.get("take_profit", 0), "confidence": conf, "justification": result.get("justification", "")[:200], "signal": result.get("signal", self.brief_data.get("signal", "NEUTRO")), "price": price, "timestamp": datetime.now().isoformat() } decision_str = f"{output['action']} | Conf: {output['confidence']}%" if output["action"] in ("BUY", "SELL"): decision_str += f" | SL: ${output['stop_loss']:,.0f} | TP: ${output['take_profit']:,.0f}" decision_str += f" | {output['justification'][:80]}" output["decision"] = decision_str output["agent"] = "Decio" return output