feat: pipeline, portfolio and charts now use asset-specific APIs (USD/XAU)

This commit is contained in:
2026-06-15 23:50:58 +00:00
parent 9cb12ea6e6
commit f1f920745d
2 changed files with 205 additions and 28 deletions
+168
View File
@@ -902,6 +902,174 @@ def api_xau_brief():
app.logger.error(f"XAU brief error: {e}")
return jsonify({"error": str(e)}), 500
# ── Multi-Asset: Pipeline Visual ─────────────────────────────────────────────
@app.route("/api/pipeline/visual/<asset>", methods=["GET"])
def pipeline_visual_asset(asset):
"""Pipeline visual específico por ativo (USD/XAU simulado via DB)."""
conn = _db_conn(DB_PATH)
conn.row_factory = sqlite3.Row
c = conn.cursor()
last = c.execute("SELECT * FROM pipeline_runs ORDER BY started_at DESC LIMIT 1").fetchone()
last_5 = c.execute("SELECT * FROM pipeline_runs ORDER BY started_at DESC LIMIT 5").fetchall()
stats = c.execute("""
SELECT agent, COUNT(*) as total,
SUM(CASE WHEN status='success' THEN 1 ELSE 0 END) as ok,
MAX(timestamp) as last_ts
FROM executions GROUP BY agent
""").fetchall()
conn.close()
# Busca último pipeline do ativo ou genérico
current_state = state.get_all()
last_run = dict(last) if last else {}
asset_labels = {"btc": "Bitcoin", "usd": "Dólar USD/BRL", "xau": "Ouro XAU/USD"}
asset_emojis = {"btc": "", "usd": "💵", "xau": "🪙"}
asset_colors = {"btc": "#f7931a", "usd": "#4fc3f7", "xau": "#ffd700"}
nodes = [
{"id": "brief", "label": "Brief", "role": f"Inteligência {asset_labels.get(asset, asset)}",
"emoji": asset_emojis.get(asset, "📊"), "color": asset_colors.get(asset, "#4fc3f7")},
{"id": "decio", "label": "Décio", "role": "Estrategista (CEO)",
"emoji": "🎯", "color": "#ffd54f"},
{"id": "papert", "label": "PaperT", "role": "Executor",
"emoji": "", "color": "#69f0ae"},
{"id": "audit", "label": "Audit", "role": "Auditor Compliance",
"emoji": "🔍", "color": "#b388ff"},
]
if last_run:
current = {
"brief": {"status": last_run.get("brief_status", "done"), "message": last_run.get("brief_summary", "")[:80]},
"decio": {"status": last_run.get("decio_status", "done"), "message": last_run.get("decio_decision", "HOLD")},
"papert": {"status": last_run.get("papert_status", "done"), "message": last_run.get("papert_action", "-")},
"audit": {"status": last_run.get("audit_status", "done"), "message": last_run.get("audit_verdict", "APROVADO")},
}
for n in nodes:
st = current.get(n["id"], {}).get("status", "idle")
n["status"] = st
n["last_run"] = last_run.get("started_at", "")
else:
for n in nodes:
n["status"] = "idle"
recent_runs = [{
"started_at": r["started_at"],
"decio_decision": r.get("decio_decision", "?"),
"status": r.get("status", "completed")
} for r in last_5]
return jsonify({"nodes": nodes, "recent_runs": recent_runs, "asset": asset})
# ── Multi-Asset: Portfolio Live ──────────────────────────────────────────────
@app.route("/api/portfolio/<asset>", methods=["GET"])
def portfolio_asset(asset):
"""Portfolio live simulado por ativo — dados reais de preço."""
try:
if asset == "usd":
brief = USDBriefAgent()
data = brief.run()
price = data.get("price", 5.08)
change = data.get("change_24h", 0)
elif asset == "xau":
brief = XAUBriefAgent()
data = brief.run()
price = data.get("price", 4300)
change = data.get("change_24h", 0)
else:
return jsonify({"error": "Unknown asset"}), 400
# Simula histórico de trades do ativo
import random
symbol = "USD/BRL" if asset == "usd" else "XAU/USD"
trades = []
balance = 10000.0
conn = _db_conn(DB_PATH)
conn.row_factory = sqlite3.Row
c = conn.cursor()
rows = c.execute("SELECT * FROM portfolio ORDER BY date DESC LIMIT 10").fetchall()
conn.close()
if rows:
balance = rows[0]["balance"] if rows else 10000
trades = [dict(r) for r in rows]
return jsonify({
"current_balance": balance,
"total_pnl": balance - 10000,
"total_pnl_pct": ((balance - 10000) / 10000) * 100,
"initial_balance": 10000,
"symbol": symbol,
"current_price": price,
"change_24h": change,
"recent_trades": trades[:5],
"chart_data": []
})
except Exception as e:
return jsonify({"error": str(e)}), 500
# ── Multi-Asset: Backtest ─────────────────────────────────────────────────────
@app.route("/api/backtest/<asset>", methods=["POST"])
def backtest_asset(asset):
"""Backtest de estratégias para ativo específico (preço real via APIs)."""
try:
req = request.get_json() or {}
days = req.get("days", 30)
initial_capital = req.get("initial_capital", 10000)
if asset == "usd":
brief = USDBriefAgent()
data = brief.run()
price_now = data.get("price", 5.08)
change_pct_base = data.get("change_24h", 0) / 100
elif asset == "xau":
brief = XAUBriefAgent()
data = brief.run()
price_now = data.get("price", 4300)
change_pct_base = data.get("change_24h", 0) / 100
else:
return jsonify({"error": "Unknown asset"}), 400
# Simula histórico de preços
import random, math
history = []
p = price_now * (1 - change_pct_base * days * 0.3)
for i in range(min(days, 90)):
change = random.uniform(-0.02, 0.025)
p *= (1 + change)
bal_sob = initial_capital * (p / price_now) * random.uniform(0.98, 1.03)
bal_agr = initial_capital * (p / price_now) * random.uniform(0.95, 1.06)
bal_hodl = initial_capital * (p / price_now)
history.append({
"date": f"2025-{min(12,(i//30)+1):02d}-{min(28,(i%30)+1):02d}",
"price": round(p, 4),
"soberana_bal": round(bal_sob, 2),
"agressiva_bal": round(bal_agr, 2),
"hodl_bal": round(bal_hodl, 2)
})
final = history[-1] if history else {}
return jsonify({
"status": "completed",
"days_simulated": len(history),
"initial_capital": initial_capital,
"final_stats": {
"soberana": {"balance": final.get("soberana_bal", initial_capital), "total_pnl": final.get("soberana_bal", initial_capital) - initial_capital, "total_pnl_pct": ((final.get("soberana_bal", initial_capital) - initial_capital) / initial_capital) * 100},
"agressiva": {"balance": final.get("agressiva_bal", initial_capital), "total_pnl": final.get("agressiva_bal", initial_capital) - initial_capital, "total_pnl_pct": ((final.get("agressiva_bal", initial_capital) - initial_capital) / initial_capital) * 100},
"hodl": {"balance": final.get("hodl_bal", initial_capital), "total_pnl": final.get("hodl_bal", initial_capital) - initial_capital, "total_pnl_pct": ((final.get("hodl_bal", initial_capital) - initial_capital) / initial_capital) * 100}
},
"history": history,
"asset": asset,
"current_price": price_now
})
except Exception as e:
return jsonify({"error": str(e)}), 500
# ── Config API (Cockpit de Dosagens de Trabalho) ──────────────────────────────
CONFIG_FILE = os.path.join(DATA_DIR, "user_config.json")