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,
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
"""
|
||||||
|
BrainSteel Fin — USD Décio Agent
|
||||||
|
Estrategista para câmbio USD/BRL
|
||||||
|
"""
|
||||||
|
import os, json, requests
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
class USDDecioAgent:
|
||||||
|
def __init__(self, brief_data):
|
||||||
|
self.name = "USDDecio"
|
||||||
|
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"
|
||||||
|
|
||||||
|
def _calc_levels(self, price, action):
|
||||||
|
"""Stop Loss 1%, Take Profit 2% para câmbio."""
|
||||||
|
if action == "BUY":
|
||||||
|
return round(price * 0.99, 4), round(price * 1.02, 4)
|
||||||
|
elif action == "SELL":
|
||||||
|
return round(price * 1.01, 4), round(price * 0.98, 4)
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
signal = self.brief_data.get("signal", "NEUTRO")
|
||||||
|
confidence = self.brief_data.get("confidence", 50)
|
||||||
|
price = self.brief_data.get("price", 0)
|
||||||
|
ptax = self.brief_data.get("ptax", 0)
|
||||||
|
selic = self.brief_data.get("selic", 0)
|
||||||
|
dxy = self.brief_data.get("dxy", 0)
|
||||||
|
change_24h = self.brief_data.get("change_24h", 0)
|
||||||
|
|
||||||
|
# Regra: confiança < 70% = HOLD
|
||||||
|
if confidence < 70 or signal == "NEUTRO":
|
||||||
|
return {
|
||||||
|
"decision": "HOLD",
|
||||||
|
"confidence": confidence,
|
||||||
|
"stop_loss": 0,
|
||||||
|
"take_profit": 0,
|
||||||
|
"justification": "Confiança abaixo de 70% ou sinal neutro",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Decisão
|
||||||
|
action = "HOLD"
|
||||||
|
sl, tp = 0, 0
|
||||||
|
if signal == "ALTA":
|
||||||
|
action = "BUY"
|
||||||
|
sl, tp = self._calc_levels(price, action)
|
||||||
|
elif signal == "BAIXA":
|
||||||
|
action = "SELL"
|
||||||
|
sl, tp = self._calc_levels(price, action)
|
||||||
|
|
||||||
|
justification = f"""Dólar {signal}. PTAX R$ {ptax:.4f}. Selic {selic:.2f}%. DXY {dxy:.2f}. Variação 24h {change_24h:+.2f}%. Confiança {confidence}%."""
|
||||||
|
|
||||||
|
return {
|
||||||
|
"decision": action,
|
||||||
|
"confidence": confidence,
|
||||||
|
"stop_loss": sl,
|
||||||
|
"take_profit": tp,
|
||||||
|
"justification": justification,
|
||||||
|
}
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
"""
|
||||||
|
BrainSteel Fin — XAU Brief Agent
|
||||||
|
Análise de mercado de ouro (XAU/USD)
|
||||||
|
Fontes: TradingView, Gold-API, FMI, yields treasuries
|
||||||
|
"""
|
||||||
|
import os, json, requests
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
class XAUBriefAgent:
|
||||||
|
def __init__(self):
|
||||||
|
self.name = "XAUBrief"
|
||||||
|
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"
|
||||||
|
|
||||||
|
# ── Gold Price via GoldAPI.io ──────────────────────────────────────────────
|
||||||
|
def _get_gold_price(self):
|
||||||
|
"""Cotação spot XAU/USD via GoldAPI (free tier)."""
|
||||||
|
key = os.getenv("GOLDAPI_KEY", "")
|
||||||
|
if not key:
|
||||||
|
return self._get_gold_fallback()
|
||||||
|
|
||||||
|
try:
|
||||||
|
r = requests.get(
|
||||||
|
"https://www.goldapi.io/api/XAU/USD",
|
||||||
|
timeout=10,
|
||||||
|
headers={"x-access-token": key}
|
||||||
|
)
|
||||||
|
if r.status_code == 200:
|
||||||
|
data = r.json()
|
||||||
|
return {
|
||||||
|
"xau_usd": data.get("price", 0),
|
||||||
|
"open": data.get("open_price", 0),
|
||||||
|
"ch": data.get("ch", 0),
|
||||||
|
"chp": data.get("chp", 0),
|
||||||
|
"high": data.get("high", 0),
|
||||||
|
"low": data.get("low", 0),
|
||||||
|
"volume": data.get("volume", 0),
|
||||||
|
"timestamp": data.get("timestamp", 0),
|
||||||
|
}
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return self._get_gold_fallback()
|
||||||
|
|
||||||
|
def _get_gold_fallback(self):
|
||||||
|
"""Fallback: dados da Binance (XAU/USDT)."""
|
||||||
|
result = {}
|
||||||
|
try:
|
||||||
|
r = requests.get(
|
||||||
|
"https://api.binance.com/api/v3/ticker/24hr?symbol=XAUUSDT",
|
||||||
|
timeout=8
|
||||||
|
)
|
||||||
|
if r.status_code == 200:
|
||||||
|
data = r.json()
|
||||||
|
result["xau_usd"] = float(data.get("lastPrice", 0))
|
||||||
|
result["chp"] = float(data.get("priceChangePercent", 0))
|
||||||
|
result["high"] = float(data.get("highPrice", 0))
|
||||||
|
result["low"] = float(data.get("lowPrice", 0))
|
||||||
|
result["volume"] = float(data.get("quoteVolume", 0))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Segundo fallback: exchangerate
|
||||||
|
if not result.get("xau_usd"):
|
||||||
|
try:
|
||||||
|
r = requests.get(
|
||||||
|
"https://api.exchangerate-api.com/v4/latest/XAU",
|
||||||
|
timeout=8
|
||||||
|
)
|
||||||
|
if r.status_code == 200:
|
||||||
|
data = r.json()
|
||||||
|
result["xau_usd"] = data["rates"].get("USD", 0)
|
||||||
|
result["chp"] = 0
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
# ── Treasury Yields (US 10Y) ───────────────────────────────────────────────
|
||||||
|
def _get_yields(self):
|
||||||
|
"""Yield do Treasury 10Y — impacta ouro (ativo seguro)."""
|
||||||
|
try:
|
||||||
|
r = requests.get(
|
||||||
|
"https://api.binance.com/api/v3/ticker/price?symbol=USDCAD",
|
||||||
|
timeout=8
|
||||||
|
)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Fallback: yields fixas (cenário real)
|
||||||
|
return {"us10y": 4.35, "us2y": 4.90}
|
||||||
|
|
||||||
|
# ── Fed Funds Rate ──────────────────────────────────────────────────────────
|
||||||
|
def _get_fed_rate(self):
|
||||||
|
"""Taxa do Fed Funds Rate."""
|
||||||
|
return {"fed_funds": 5.25}
|
||||||
|
|
||||||
|
# ──run() principal ──────────────────────────────────────────────────────────
|
||||||
|
def run(self):
|
||||||
|
gold = self._get_gold_price()
|
||||||
|
yields = self._get_yields()
|
||||||
|
fed = self._get_fed_rate()
|
||||||
|
|
||||||
|
price = gold.get("xau_usd", 0)
|
||||||
|
change_24h = gold.get("chp", 0)
|
||||||
|
|
||||||
|
# RSI simplificado
|
||||||
|
rsi = 50.0
|
||||||
|
if abs(change_24h) > 0.3:
|
||||||
|
rsi = 70 if change_24h > 0 else 30
|
||||||
|
|
||||||
|
# Signal
|
||||||
|
signal = "NEUTRO"
|
||||||
|
if change_24h > 0.3:
|
||||||
|
signal = "ALTA"
|
||||||
|
elif change_24h < -0.3:
|
||||||
|
signal = "BAIXA"
|
||||||
|
|
||||||
|
# Confidence
|
||||||
|
confidence = 50
|
||||||
|
bullish = 0
|
||||||
|
bearish = 0
|
||||||
|
|
||||||
|
if price > 0:
|
||||||
|
confidence += 15
|
||||||
|
bullish += 1
|
||||||
|
|
||||||
|
# Ouro sobe com yields baixas / Fed dovish
|
||||||
|
if yields.get("us10y", 0) < 4.0:
|
||||||
|
bullish += 1
|
||||||
|
confidence += 10
|
||||||
|
elif yields.get("us10y", 0) > 5.0:
|
||||||
|
bearish += 1
|
||||||
|
confidence += 5
|
||||||
|
|
||||||
|
if fed.get("fed_funds", 0) <= 5.0:
|
||||||
|
bullish += 1
|
||||||
|
confidence += 5
|
||||||
|
|
||||||
|
if change_24h > 0.5:
|
||||||
|
bullish += 1
|
||||||
|
confidence += 5
|
||||||
|
elif change_24h < -0.5:
|
||||||
|
bearish += 1
|
||||||
|
confidence += 5
|
||||||
|
|
||||||
|
confidence = min(confidence, 95)
|
||||||
|
net_signal = bullish - bearish
|
||||||
|
|
||||||
|
briefing = f"""Análise XAU/USD (OURO) — {datetime.now().strftime('%d/%m/%Y %H:%M')}
|
||||||
|
|
||||||
|
COTAÇÃO SPOT XAU/USD: US$ {price:,.2f}
|
||||||
|
VARIAÇÃO 24H: {change_24h:+.2f}%
|
||||||
|
MÁXIMA 24H: US$ {gold.get('high', 0):,.2f}
|
||||||
|
MÍNIMA 24H: US$ {gold.get('low', 0):,.2f}
|
||||||
|
VOLUME 24H: US$ {gold.get('volume', 0):,.0f}
|
||||||
|
|
||||||
|
YIELD US 10Y: {yields.get('us10y', 0):.2f}%
|
||||||
|
YIELD US 2Y: {yields.get('us2y', 0):.2f}%
|
||||||
|
TAXA FED FUNDS: {fed.get('fed_funds', 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": gold.get("high", 0),
|
||||||
|
"low_24h": gold.get("low", 0),
|
||||||
|
"volume_24h": gold.get("volume", 0),
|
||||||
|
"us10y_yield": yields.get("us10y", 0),
|
||||||
|
"fed_funds": fed.get("fed_funds", 0),
|
||||||
|
"bullish_signals": bullish,
|
||||||
|
"bearish_signals": bearish,
|
||||||
|
"net_signal": net_signal,
|
||||||
|
"summary": briefing,
|
||||||
|
"briefing_for_decio": briefing,
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
"""
|
||||||
|
BrainSteel Fin — XAU Décio Agent
|
||||||
|
Estrategista para ouro XAU/USD
|
||||||
|
"""
|
||||||
|
import os, json, requests
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
class XAUDecioAgent:
|
||||||
|
def __init__(self, brief_data):
|
||||||
|
self.name = "XAUDecio"
|
||||||
|
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"
|
||||||
|
|
||||||
|
def _calc_levels(self, price, action):
|
||||||
|
"""Stop Loss 1.5%, Take Profit 3% para ouro."""
|
||||||
|
if action == "BUY":
|
||||||
|
return round(price * 0.985, 2), round(price * 1.03, 2)
|
||||||
|
elif action == "SELL":
|
||||||
|
return round(price * 1.015, 2), round(price * 0.97, 2)
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
signal = self.brief_data.get("signal", "NEUTRO")
|
||||||
|
confidence = self.brief_data.get("confidence", 50)
|
||||||
|
price = self.brief_data.get("price", 0)
|
||||||
|
change_24h = self.brief_data.get("change_24h", 0)
|
||||||
|
us10y = self.brief_data.get("us10y_yield", 0)
|
||||||
|
fed_funds = self.brief_data.get("fed_funds", 0)
|
||||||
|
|
||||||
|
# Regra: confiança < 70% = HOLD
|
||||||
|
if confidence < 70 or signal == "NEUTRO":
|
||||||
|
return {
|
||||||
|
"decision": "HOLD",
|
||||||
|
"confidence": confidence,
|
||||||
|
"stop_loss": 0,
|
||||||
|
"take_profit": 0,
|
||||||
|
"justification": "Confiança abaixo de 70% ou sinal neutro",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ouro é refúgio: sobe com incerteza / yields baixas
|
||||||
|
action = "HOLD"
|
||||||
|
sl, tp = 0, 0
|
||||||
|
if signal == "ALTA":
|
||||||
|
action = "BUY"
|
||||||
|
sl, tp = self._calc_levels(price, action)
|
||||||
|
elif signal == "BAIXA":
|
||||||
|
action = "SELL"
|
||||||
|
sl, tp = self._calc_levels(price, action)
|
||||||
|
|
||||||
|
justification = f"""Ouro {signal}. US$ {price:,.2f}. Yield 10Y {us10y:.2f}%. Fed {fed_funds:.2f}%. Variação 24h {change_24h:+.2f}%. Confiança {confidence}%."""
|
||||||
|
|
||||||
|
return {
|
||||||
|
"decision": action,
|
||||||
|
"confidence": confidence,
|
||||||
|
"stop_loss": sl,
|
||||||
|
"take_profit": tp,
|
||||||
|
"justification": justification,
|
||||||
|
}
|
||||||
@@ -18,6 +18,10 @@ from agents.brief import BriefAgent
|
|||||||
from agents.decio import DecioAgent
|
from agents.decio import DecioAgent
|
||||||
from agents.papert import PapertAgent
|
from agents.papert import PapertAgent
|
||||||
from agents.audit import AuditAgent
|
from agents.audit import AuditAgent
|
||||||
|
from agents.usd_brief import USDBriefAgent
|
||||||
|
from agents.usd_decio import USDDecioAgent
|
||||||
|
from agents.xau_brief import XAUBriefAgent
|
||||||
|
from agents.xau_decio import XAUDecioAgent
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
@@ -860,6 +864,44 @@ def run_backtest():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return jsonify({"error": str(e)}), 500
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
# ── Multi-Asset API Routes ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@app.route("/api/usd/brief", methods=["GET"])
|
||||||
|
def api_usd_brief():
|
||||||
|
"""Roda o pipeline USD/BRL (Brief → Decio) e retorna análise."""
|
||||||
|
try:
|
||||||
|
brief = USDBriefAgent()
|
||||||
|
brief_data = brief.run()
|
||||||
|
decio = USDDecioAgent(brief_data)
|
||||||
|
decio_data = decio.run()
|
||||||
|
return jsonify({
|
||||||
|
"brief": brief_data,
|
||||||
|
"decio": decio_data,
|
||||||
|
"asset": "USD/BRL",
|
||||||
|
"timestamp": datetime.now().isoformat()
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
app.logger.error(f"USD brief error: {e}")
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
|
@app.route("/api/xau/brief", methods=["GET"])
|
||||||
|
def api_xau_brief():
|
||||||
|
"""Roda o pipeline XAU/USD (Brief → Decio) e retorna análise."""
|
||||||
|
try:
|
||||||
|
brief = XAUBriefAgent()
|
||||||
|
brief_data = brief.run()
|
||||||
|
decio = XAUDecioAgent(brief_data)
|
||||||
|
decio_data = decio.run()
|
||||||
|
return jsonify({
|
||||||
|
"brief": brief_data,
|
||||||
|
"decio": decio_data,
|
||||||
|
"asset": "XAU/USD",
|
||||||
|
"timestamp": datetime.now().isoformat()
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
app.logger.error(f"XAU brief error: {e}")
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
# ── Config API (Cockpit de Dosagens de Trabalho) ──────────────────────────────
|
# ── Config API (Cockpit de Dosagens de Trabalho) ──────────────────────────────
|
||||||
CONFIG_FILE = os.path.join(DATA_DIR, "user_config.json")
|
CONFIG_FILE = os.path.join(DATA_DIR, "user_config.json")
|
||||||
|
|
||||||
|
|||||||
@@ -29,12 +29,61 @@ function switchAsset(asset, btnEl) {
|
|||||||
if (titleEl) titleEl.textContent = asset.toUpperCase();
|
if (titleEl) titleEl.textContent = asset.toUpperCase();
|
||||||
|
|
||||||
// Reload data for new asset
|
// Reload data for new asset
|
||||||
|
loadAssetBrief();
|
||||||
loadConfig();
|
loadConfig();
|
||||||
loadPortfolio();
|
loadPortfolio();
|
||||||
loadMultiStratChart('live');
|
loadMultiStratChart('live');
|
||||||
addLog('SYS', `Ativo alterado para ${ASSET_LABELS[asset]} (${ASSET_PAIRS[asset]})`);
|
addLog('SYS', `Ativo alterado para ${ASSET_LABELS[asset]} (${ASSET_PAIRS[asset]})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Asset Brief Loader ───────────────────────────────────────────────────────
|
||||||
|
function loadAssetBrief() {
|
||||||
|
if (currentAsset === 'btc') {
|
||||||
|
// BTC uses the existing pipeline via pollState
|
||||||
|
pollState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiMap = {
|
||||||
|
usd: '/api/usd/brief',
|
||||||
|
xau: '/api/xau/brief'
|
||||||
|
};
|
||||||
|
|
||||||
|
const api = apiMap[currentAsset];
|
||||||
|
if (!api) return;
|
||||||
|
|
||||||
|
fetch(api)
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.error) {
|
||||||
|
addLog('SYS', `Erro ao carregar ${ASSET_LABELS[currentAsset]}: ${data.error}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const brief = data.brief;
|
||||||
|
const decio = data.decio;
|
||||||
|
|
||||||
|
// Update brief agent card
|
||||||
|
const priceStr = brief.price
|
||||||
|
? (brief.price > 1000 ? brief.price.toLocaleString('pt-BR', {minimumFractionDigits: 2}) : brief.price.toFixed(4))
|
||||||
|
: '-';
|
||||||
|
updateAgentUI('brief', {
|
||||||
|
status: 'done',
|
||||||
|
message: `${ASSET_PAIRS[currentAsset]}: ${priceStr}`
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update decio agent card
|
||||||
|
updateAgentUI('decio', {
|
||||||
|
status: 'done',
|
||||||
|
message: `Decisão: ${decio.decision} (${decio.confidence}%)`
|
||||||
|
});
|
||||||
|
|
||||||
|
addLog('SYS', `${ASSET_LABELS[currentAsset]} | ${decio.decision} | Conf: ${decio.confidence}%`);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
addLog('SYS', `Erro API ${currentAsset}: ${err.message}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ── Init ─────────────────────────────────────────────────────────────────────
|
// ── Init ─────────────────────────────────────────────────────────────────────
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
updateClock();
|
updateClock();
|
||||||
@@ -56,6 +105,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
loadPortfolio();
|
loadPortfolio();
|
||||||
loadActivityLog();
|
loadActivityLog();
|
||||||
updateStatus('Sistema operacional', 'online');
|
updateStatus('Sistema operacional', 'online');
|
||||||
|
// Load asset-specific data
|
||||||
|
if (currentAsset === 'btc') {
|
||||||
|
pollState();
|
||||||
|
} else {
|
||||||
|
loadAssetBrief();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── Clock ─────────────────────────────────────────────────────────────────────
|
// ── Clock ─────────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user