BrainSteel Fin v1.0 — 3-agent BTC trading pipeline

- Brief: Market Intelligence (Binance data + LLM analysis)
- Decio: Strategy decision (BUY/HOLD/SELL)
- PaperT: Order executor (Binance API)
- Anime-style Flask dashboard
- Traefik-ready Docker deployment
This commit is contained in:
Hermes - BrainSteel VPS
2026-05-17 16:25:55 +00:00
commit 50960bab21
24 changed files with 3285 additions and 0 deletions
+182
View File
@@ -0,0 +1,182 @@
"""
BrainSteel Fin — Decio Agent (v2)
Chief Strategist & Risk Officer
5-Pillar decision engine: Confiança < 75% → 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 < 75% → always HOLD ─────────────────────────
def _risk_filter(self, confidence, signal):
"""Décio is a mathematician: no confidence ≥75%, no operation."""
return confidence < 75 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 < 75% → decisão SEMPRE HOLD (nunca opera)
Confiança ≥ 75% + sinal=ALTA → BUY
Confiança ≥ 75% + 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 < 75% — 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}% < 75% — 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
}
# ── 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)
output = {
"action": result["decision"],
"amount_pct": 25 if result["decision"] in ("BUY", "SELL") else 0,
"stop_loss": result.get("stop_loss", 0),
"take_profit": result.get("take_profit", 0),
"confidence": result.get("confidence", 50),
"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