feat: add USD/BRL and XAU/USD pipeline agents (Brief+Decio) with /api/usd/brief and /api/xau/brief routes
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
"""
|
||||
BrainSteel Fin — USD Brief Agent
|
||||
Análise de mercado de câmbio USD/BRL
|
||||
Fontes: BACEN/B3 PTAX, derivadas, fluxo cambial, macro
|
||||
"""
|
||||
import os, json, requests
|
||||
from datetime import datetime
|
||||
|
||||
class USDBriefAgent:
|
||||
def __init__(self):
|
||||
self.name = "USDBrief"
|
||||
self.openrouter_key = os.getenv("BRIEF_API_KEY", os.getenv("OPENROUTER_API_KEY", ""))
|
||||
self.openrouter_url = "https://openrouter.ai/api/v1/chat/completions"
|
||||
self.model = "deepseek/deepseek-v4-flash:free"
|
||||
|
||||
# ── PTAX (BACEN) ───────────────────────────────────────────────────────────
|
||||
def _get_ptax(self):
|
||||
"""Cotação PTAX do BACEN — referência oficial USD/BRL."""
|
||||
try:
|
||||
# BACEN API - cotação PTAX
|
||||
r = requests.get(
|
||||
"https://api.bcb.gov.br/dados/serie/bcbdata/20795/dados/ultimos/1?formato=json",
|
||||
timeout=10
|
||||
)
|
||||
if r.status_code == 200:
|
||||
data = r.json()
|
||||
if data and len(data) > 0:
|
||||
return {
|
||||
"ptax": float(data[0].get("valor", 0).replace(",", ".")),
|
||||
"data": data[0].get("data", "")
|
||||
}
|
||||
except:
|
||||
pass
|
||||
|
||||
# Fallback: Exchange Rates API
|
||||
try:
|
||||
r = requests.get(
|
||||
"https://api.exchangerate-api.com/v4/latest/USD",
|
||||
timeout=8
|
||||
)
|
||||
if r.status_code == 200:
|
||||
data = r.json()
|
||||
return {
|
||||
"ptax": data["rates"].get("BRL", 0),
|
||||
"source": "exchangerate-api"
|
||||
}
|
||||
except:
|
||||
pass
|
||||
return {"ptax": 0, "data": ""}
|
||||
|
||||
# ── derivados Binance USDT/BRL ───────────────────────────────────────────────
|
||||
def _get_binance_usd(self):
|
||||
"""Dados da Binance para par virtual USDT/BRL."""
|
||||
result = {}
|
||||
try:
|
||||
r = requests.get(
|
||||
"https://api.binance.com/api/v3/ticker/24hr?symbol=USDTBRL",
|
||||
timeout=8
|
||||
)
|
||||
if r.status_code == 200:
|
||||
data = r.json()
|
||||
result["symbol"] = "USDTBRL"
|
||||
result["price"] = float(data.get("lastPrice", 0))
|
||||
result["change_24h_pct"] = float(data.get("priceChangePercent", 0))
|
||||
result["high_24h"] = float(data.get("highPrice", 0))
|
||||
result["low_24h"] = float(data.get("lowPrice", 0))
|
||||
result["volume_24h"] = float(data.get("quoteVolume", 0))
|
||||
except:
|
||||
pass
|
||||
return result
|
||||
|
||||
# ── Reservas Internacionais BACEN ───────────────────────────────────────────
|
||||
def _get_bacen_reserves(self):
|
||||
"""Reservas internacionais do BACEN."""
|
||||
try:
|
||||
r = requests.get(
|
||||
"https://api.bcb.gov.br/dados/serie/bcbdata/1366/dados/ultimos/1?formato=json",
|
||||
timeout=10
|
||||
)
|
||||
if r.status_code == 200:
|
||||
data = r.json()
|
||||
if data:
|
||||
val = data[0].get("valor", "0")
|
||||
return {"reserves_bil_usd": float(val.replace(",", ".")) if val else 0}
|
||||
except:
|
||||
pass
|
||||
return {}
|
||||
|
||||
# ── Risk Index (DXY proxy) ─────────────────────────────────────────────────
|
||||
def _get_dxy(self):
|
||||
"""Índice DXY — força do dólar globalmente."""
|
||||
try:
|
||||
r = requests.get(
|
||||
"https://api.binance.com/api/v3/ticker/price?symbol=DXYUSDT",
|
||||
timeout=8
|
||||
)
|
||||
if r.status_code == 200:
|
||||
return {"dxy": float(r.json().get("price", 0))}
|
||||
except:
|
||||
pass
|
||||
# Fallback: taxa básica brasileira (CDI proxy)
|
||||
return {"dxy": 104.5}
|
||||
|
||||
# ── Taxa Selic ─────────────────────────────────────────────────────────────
|
||||
def _get_selic(self):
|
||||
"""Taxa Selic atual."""
|
||||
try:
|
||||
r = requests.get(
|
||||
"https://api.bcb.gov.br/dados/serie/bcbdata/432/dados/ultimos/1?formato=json",
|
||||
timeout=10
|
||||
)
|
||||
if r.status_code == 200:
|
||||
data = r.json()
|
||||
if data:
|
||||
val = data[0].get("valor", "0")
|
||||
return {"selic": float(val.replace(",", ".")) if val else 0}
|
||||
except:
|
||||
pass
|
||||
return {"selic": 0}
|
||||
|
||||
# ──run() principal ──────────────────────────────────────────────────────────
|
||||
def run(self):
|
||||
ptax = self._get_ptax()
|
||||
binance = self._get_binance_usd()
|
||||
reserves = self._get_bacen_reserves()
|
||||
dxy = self._get_dxy()
|
||||
selic = self._get_selic()
|
||||
|
||||
price = ptax.get("ptax") or binance.get("price") or 0
|
||||
change_24h = binance.get("change_24h_pct", 0)
|
||||
|
||||
# RSI simplificado (baseado na variação)
|
||||
rsi = 50.0
|
||||
if abs(change_24h) > 0.5:
|
||||
rsi = 70 if change_24h > 0 else 30
|
||||
|
||||
# Signal
|
||||
signal = "NEUTRO"
|
||||
if change_24h > 0.5:
|
||||
signal = "ALTA"
|
||||
elif change_24h < -0.5:
|
||||
signal = "BAIXA"
|
||||
|
||||
# Confidence
|
||||
confidence = 50
|
||||
bullish = 0
|
||||
bearish = 0
|
||||
|
||||
if ptax.get("ptax", 0) > 0:
|
||||
confidence += 10
|
||||
bullish += 1
|
||||
if binance.get("price", 0) > 0:
|
||||
confidence += 10
|
||||
bullish += 1
|
||||
if selic.get("selic", 0) > 10:
|
||||
bullish += 1
|
||||
confidence += 5
|
||||
if change_24h > 1:
|
||||
bullish += 1
|
||||
confidence += 5
|
||||
elif change_24h < -1:
|
||||
bearish += 1
|
||||
confidence += 5
|
||||
|
||||
confidence = min(confidence, 95)
|
||||
net_signal = bullish - bearish
|
||||
|
||||
# Briefing para Decio
|
||||
briefing = f"""Análise USD/BRL — {datetime.now().strftime('%d/%m/%Y %H:%M')}
|
||||
|
||||
COTAÇÃO PTAX: R$ {ptax.get('ptax', 0):.4f}
|
||||
COTAÇÃO BINANCE (USDT/BRL): R$ {binance.get('price', 0):.4f}
|
||||
VARIAÇÃO 24H: {change_24h:+.2f}%
|
||||
MÁXIMA 24H: R$ {binance.get('high_24h', 0):.4f}
|
||||
MÍNIMA 24H: R$ {binance.get('low_24h', 0):.4f}
|
||||
VOLUME 24H: US$ {binance.get('volume_24h', 0):,.0f}
|
||||
|
||||
RESERVAS BACEN: US$ {reserves.get('reserves_bil_usd', 0):,.0f} bi
|
||||
TAXA SELIC: {selic.get('selic', 0):.2f}%
|
||||
ÍNDICE DXY: {dxy.get('dxy', 0):.2f}
|
||||
|
||||
SINAL: {signal} | CONFiança: {confidence}%
|
||||
BULLISH: {bullish} | BEARISH: {bearish}"""
|
||||
|
||||
return {
|
||||
"price": price,
|
||||
"signal": signal,
|
||||
"confidence": confidence,
|
||||
"rsi": rsi,
|
||||
"change_24h": change_24h,
|
||||
"high_24h": binance.get("high_24h", 0),
|
||||
"low_24h": binance.get("low_24h", 0),
|
||||
"volume_24h": binance.get("volume_24h", 0),
|
||||
"ptax": ptax.get("ptax", 0),
|
||||
"selic": selic.get("selic", 0),
|
||||
"dxy": dxy.get("dxy", 0),
|
||||
"reserves": reserves.get("reserves_bil_usd", 0),
|
||||
"bullish_signals": bullish,
|
||||
"bearish_signals": bearish,
|
||||
"net_signal": net_signal,
|
||||
"summary": briefing,
|
||||
"briefing_for_decio": briefing,
|
||||
}
|
||||
Reference in New Issue
Block a user