🚀 Auto-deploy: Add background automated scheduler, Kelly allocation, multi-portfolio simulations, and UI improvements
This commit is contained in:
Binary file not shown.
Binary file not shown.
+50
-2
@@ -10,6 +10,52 @@ class AuditAgent:
|
||||
def __init__(self):
|
||||
self.name = "Audit"
|
||||
|
||||
def send_notification(self, audit_result):
|
||||
"""Envia notificação ativa para Telegram/Discord ou grava no log de governança."""
|
||||
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
action = audit_result.get("decio_decision", "HOLD")
|
||||
score = audit_result.get("compliance_score", 0)
|
||||
status = audit_result.get("status", "unknown").upper()
|
||||
pnl_pct = audit_result.get("portfolio_pnl_pct", 0)
|
||||
bal = audit_result.get("portfolio_balance", 0)
|
||||
|
||||
msg = (
|
||||
f"🔔 [BrainSteel Fin Governança] — {now}\n"
|
||||
f"Decisão do Ciclo: {action}\n"
|
||||
f"Veredito do Audit: {status} (Score: {score}%)\n"
|
||||
f"Saldo Virtual: ${bal:,.2f} | PnL Total: {pnl_pct:+.1f}%\n"
|
||||
f"Notas de Qualidade:\n" + "\n".join([f"• {n}" for n in audit_result.get("quality_notes", [])])
|
||||
)
|
||||
|
||||
# 1. Envia Telegram se configurado
|
||||
tg_token = os.getenv("TELEGRAM_BOT_TOKEN", "")
|
||||
tg_chat = os.getenv("TELEGRAM_CHAT_ID", "")
|
||||
if tg_token and tg_chat:
|
||||
try:
|
||||
requests.post(
|
||||
f"https://api.telegram.org/bot{tg_token}/sendMessage",
|
||||
json={"chat_id": tg_chat, "text": msg},
|
||||
timeout=10
|
||||
)
|
||||
except: pass
|
||||
|
||||
# 2. Envia Discord Webhook se configurado
|
||||
discord_url = os.getenv("DISCORD_WEBHOOK_URL", "")
|
||||
if discord_url:
|
||||
try:
|
||||
requests.post(discord_url, json={"content": msg}, timeout=10)
|
||||
except: pass
|
||||
|
||||
# 3. Sempre grava no log de notificações de governança
|
||||
for path in ["/app/logs/notifications.log", "/data/notifications.log"]:
|
||||
try:
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
with open(path, "a", encoding="utf-8") as f:
|
||||
f.write(msg + "\n" + "-"*40 + "\n")
|
||||
except: pass
|
||||
|
||||
return msg
|
||||
|
||||
def audit_pipeline(self, brief_data, decio_data, papert_data):
|
||||
checks = []
|
||||
|
||||
@@ -105,7 +151,7 @@ class AuditAgent:
|
||||
if decio_action == "HOLD" and brief_data.get("confidence", 0) < 75:
|
||||
quality_notes.append("HOLD correto — confiança baixa preservou capital")
|
||||
|
||||
return {
|
||||
res = {
|
||||
"audit_id": f"AUD-{datetime.now().strftime('%Y%m%d%H%M%S')}",
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"checks": checks,
|
||||
@@ -117,10 +163,12 @@ class AuditAgent:
|
||||
"brief_summary": brief_data.get("summary", "")[:100],
|
||||
"decio_decision": decio_action,
|
||||
"decio_confidence": decio_conf,
|
||||
"portfolio_balance": portfolio.get("current_balance", 0),
|
||||
"portfolio_balance": portfolio.get("current_balance") or portfolio.get("balance", 0),
|
||||
"portfolio_pnl": portfolio.get("total_pnl", 0),
|
||||
"portfolio_pnl_pct": portfolio.get("total_pnl_pct", 0),
|
||||
}
|
||||
self.send_notification(res)
|
||||
return res
|
||||
|
||||
def generate_llm_report(self, brief_data, decio_data):
|
||||
if not os.getenv("PAPERT_API_KEY") and not os.getenv("OPENROUTER_API_KEY", ""):
|
||||
|
||||
+26
-10
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
BrainSteel Fin — Decio Agent (v2)
|
||||
Chief Strategist & Risk Officer
|
||||
5-Pillar decision engine: Confiança < 75% → HOLD. BUY/SELL → SL 2% / TP 5%.
|
||||
5-Pillar decision engine: Confiança < 70% → HOLD. BUY/SELL → SL 2% / TP 5%.
|
||||
"""
|
||||
import os, json, requests
|
||||
from datetime import datetime
|
||||
@@ -14,7 +14,7 @@ class DecioAgent:
|
||||
self.openrouter_url = "https://openrouter.ai/api/v1/chat/completions"
|
||||
self.model = "deepseek/deepseek-v4-flash:free"
|
||||
|
||||
# ── CORE RULE: confidence < 75% → always HOLD ─────────────────────────
|
||||
# ── 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")
|
||||
@@ -62,9 +62,9 @@ Analítico, frio, matemático. Preservação de capital > lucro rápido.
|
||||
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
|
||||
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
|
||||
|
||||
@@ -102,7 +102,7 @@ Analítico, frio, matemático. Preservação de capital > lucro rápido.
|
||||
# Apply risk filter
|
||||
if self._risk_filter(data.get("confidence", 0), signal):
|
||||
data["decision"] = "HOLD"
|
||||
data["justification"] = "Confiança < 75% — preservando capital"
|
||||
data["justification"] = "Confiança < 70% — preservando capital"
|
||||
data["stop_loss"] = 0
|
||||
data["take_profit"] = 0
|
||||
return data
|
||||
@@ -131,7 +131,7 @@ Analítico, frio, matemático. Preservação de capital > lucro rápido.
|
||||
elif self._risk_filter(confidence, signal):
|
||||
decision = "HOLD"
|
||||
sl, tp = 0, 0
|
||||
justification = f"Confiança {confidence}% < 75% — preservando capital"
|
||||
justification = f"Confiança {confidence}% < 70% — preservando capital"
|
||||
elif signal == "ALTA":
|
||||
decision = "BUY"
|
||||
sl, tp = self._calc_levels(price, "BUY")
|
||||
@@ -154,18 +154,34 @@ Analítico, frio, matemático. Preservação de capital > lucro rápido.
|
||||
"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": result["decision"],
|
||||
"amount_pct": 25 if result["decision"] in ("BUY", "SELL") else 0,
|
||||
"action": action,
|
||||
"amount_pct": amount_pct,
|
||||
"stop_loss": result.get("stop_loss", 0),
|
||||
"take_profit": result.get("take_profit", 0),
|
||||
"confidence": result.get("confidence", 50),
|
||||
"confidence": conf,
|
||||
"justification": result.get("justification", "")[:200],
|
||||
"signal": result.get("signal", self.brief_data.get("signal", "NEUTRO")),
|
||||
"price": price,
|
||||
|
||||
+83
-46
@@ -13,17 +13,22 @@ DATA_DIR = Path("/app/data")
|
||||
PORTFOLIO_DB = DATA_DIR / "portfolio.db"
|
||||
|
||||
class Portfolio:
|
||||
"""Portfolio simulation — tracking de saldo, trades e P&L."""
|
||||
"""Portfolio simulation — tracking de saldo, trades e P&L (Multi-Estratégia)."""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, strategy="soberana"):
|
||||
self.strategy = strategy
|
||||
self.suf = "" if strategy == "soberana" else f"_{strategy}"
|
||||
self.tbl_portfolio = f"portfolio{self.suf}"
|
||||
self.tbl_trades = f"trades{self.suf}"
|
||||
self.tbl_snapshot = f"daily_snapshot{self.suf}"
|
||||
self._init_db()
|
||||
|
||||
def _init_db(self):
|
||||
DATA_DIR.mkdir(parents=True, exist_ok=True)
|
||||
conn = sqlite3.connect(str(PORTFOLIO_DB))
|
||||
c = conn.cursor()
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS portfolio (
|
||||
c.execute(f"""
|
||||
CREATE TABLE IF NOT EXISTS {self.tbl_portfolio} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
date TEXT,
|
||||
balance REAL,
|
||||
@@ -36,8 +41,8 @@ class Portfolio:
|
||||
UNIQUE(date)
|
||||
)
|
||||
""")
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS trades (
|
||||
c.execute(f"""
|
||||
CREATE TABLE IF NOT EXISTS {self.tbl_trades} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
date TEXT,
|
||||
action TEXT,
|
||||
@@ -55,8 +60,8 @@ class Portfolio:
|
||||
created_at TEXT
|
||||
)
|
||||
""")
|
||||
c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS daily_snapshot (
|
||||
c.execute(f"""
|
||||
CREATE TABLE IF NOT EXISTS {self.tbl_snapshot} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
date TEXT UNIQUE,
|
||||
balance REAL,
|
||||
@@ -72,13 +77,13 @@ class Portfolio:
|
||||
""")
|
||||
# Ensure today's row exists
|
||||
today = date.today().isoformat()
|
||||
row = c.execute("SELECT balance FROM portfolio WHERE date=?", (today,)).fetchone()
|
||||
row = c.execute(f"SELECT balance FROM {self.tbl_portfolio} WHERE date=?", (today,)).fetchone()
|
||||
if not row:
|
||||
# Seed from last known balance or initial
|
||||
last = c.execute("SELECT balance, btc_held FROM portfolio ORDER BY date DESC LIMIT 1").fetchone()
|
||||
last = c.execute(f"SELECT balance, btc_held FROM {self.tbl_portfolio} ORDER BY date DESC LIMIT 1").fetchone()
|
||||
balance = last[0] if last else INITIAL_BALANCE
|
||||
btc_held = last[1] if last else 0.0
|
||||
c.execute("INSERT INTO portfolio (date,balance,btc_held,created_at) VALUES (?,?,?,?)",
|
||||
c.execute(f"INSERT INTO {self.tbl_portfolio} (date,balance,btc_held,created_at) VALUES (?,?,?,?)",
|
||||
(today, balance, btc_held, datetime.now().isoformat()))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
@@ -88,7 +93,7 @@ class Portfolio:
|
||||
conn = sqlite3.connect(str(PORTFOLIO_DB))
|
||||
c = conn.cursor()
|
||||
today = date.today().isoformat()
|
||||
row = c.execute("SELECT balance, btc_held FROM portfolio WHERE date=?", (today,)).fetchone()
|
||||
row = c.execute(f"SELECT balance, btc_held FROM {self.tbl_portfolio} WHERE date=?", (today,)).fetchone()
|
||||
conn.close()
|
||||
return {"balance": row[0] if row else INITIAL_BALANCE, "btc_held": row[1] if row else 0.0}
|
||||
|
||||
@@ -127,7 +132,7 @@ class Portfolio:
|
||||
new_btc_held = btc_held + btc_amount
|
||||
amount_usdt = allocation
|
||||
exit_reason = "entry"
|
||||
result_str = f"🟢 BUY | ${allocation:.2f} alocado | {btc_amount:.6f} BTC @ ${price:,.0f} | Fee: ${fee:.2f}"
|
||||
result_str = f"🟢 BUY ({self.strategy}) | ${allocation:.2f} alocado | {btc_amount:.6f} BTC @ ${price:,.0f} | Fee: ${fee:.2f}"
|
||||
|
||||
elif action == "SELL" and btc_held > 0:
|
||||
# Sell portion of BTC held
|
||||
@@ -150,19 +155,19 @@ class Portfolio:
|
||||
elif take_profit and price >= take_profit:
|
||||
exit_reason = "take_profit_hit"
|
||||
else:
|
||||
exit_reason = "decio_signal"
|
||||
exit_reason = f"signal_{self.strategy}"
|
||||
|
||||
result_str = f"🔴 SELL | {btc_to_sell:.6f} BTC @ ${price:,.0f} | Net: ${net_proceeds:.2f} | Fee: ${fee:.2f}"
|
||||
result_str = f"🔴 SELL ({self.strategy}) | {btc_to_sell:.6f} BTC @ ${price:,.0f} | Net: ${net_proceeds:.2f} | Fee: ${fee:.2f}"
|
||||
else:
|
||||
new_balance = balance
|
||||
new_btc_held = btc_held
|
||||
result_str = f"⚪ SELL ignorado | BTC held: {btc_held:.6f}"
|
||||
result_str = f"⚪ SELL ignorado ({self.strategy}) | BTC held: {btc_held:.6f}"
|
||||
|
||||
# Record trade if it happened
|
||||
if action in ("BUY", "SELL") and (action == "BUY" or btc_held > 0):
|
||||
is_win = pnl > 0 if action == "SELL" and btc_held > 0 else None
|
||||
c.execute("""
|
||||
INSERT INTO trades (date,action,entry_price,amount_usdt,btc_amount,exit_price,pnl_usdt,pnl_pct,stop_loss,take_profit,fee_usdt,exit_reason,result,created_at)
|
||||
c.execute(f"""
|
||||
INSERT INTO {self.tbl_trades} (date,action,entry_price,amount_usdt,btc_amount,exit_price,pnl_usdt,pnl_pct,stop_loss,take_profit,fee_usdt,exit_reason,result,created_at)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
""", (today, action, price, amount_usdt, btc_amount,
|
||||
price if action == "SELL" else 0,
|
||||
@@ -172,12 +177,12 @@ class Portfolio:
|
||||
now))
|
||||
|
||||
# Update daily stats
|
||||
c.execute("UPDATE portfolio SET balance=?, btc_held=?, total_trades=total_trades+1 WHERE date=?",
|
||||
c.execute(f"UPDATE {self.tbl_portfolio} SET balance=?, btc_held=?, total_trades=total_trades+1 WHERE date=?",
|
||||
(new_balance, new_btc_held, today))
|
||||
if is_win is True:
|
||||
c.execute("UPDATE portfolio SET wins=wins+1 WHERE date=?", (today,))
|
||||
c.execute(f"UPDATE {self.tbl_portfolio} SET wins=wins+1 WHERE date=?", (today,))
|
||||
elif is_win is False:
|
||||
c.execute("UPDATE portfolio SET losses=losses+1 WHERE date=?", (today,))
|
||||
c.execute(f"UPDATE {self.tbl_portfolio} SET losses=losses+1 WHERE date=?", (today,))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
@@ -196,7 +201,7 @@ class Portfolio:
|
||||
c = conn.cursor()
|
||||
|
||||
# All trades
|
||||
trades = c.execute("SELECT action,pnl_usdt,pnl_pct,result,date FROM trades ORDER BY created_at DESC").fetchall()
|
||||
trades = c.execute(f"SELECT action,pnl_usdt,pnl_pct,result,date FROM {self.tbl_trades} ORDER BY created_at DESC").fetchall()
|
||||
|
||||
total_trades = len(trades)
|
||||
wins = sum(1 for t in trades if t[3] == "WIN")
|
||||
@@ -204,7 +209,7 @@ class Portfolio:
|
||||
|
||||
# Current balance
|
||||
today = date.today().isoformat()
|
||||
cur = c.execute("SELECT balance, btc_held FROM portfolio WHERE date=?", (today,)).fetchone()
|
||||
cur = c.execute(f"SELECT balance, btc_held FROM {self.tbl_portfolio} WHERE date=?", (today,)).fetchone()
|
||||
current_balance = cur[0] if cur else INITIAL_BALANCE
|
||||
btc_held = cur[1] if cur else 0.0
|
||||
|
||||
@@ -217,7 +222,7 @@ class Portfolio:
|
||||
total_value = current_balance + (btc_held * btc_price)
|
||||
|
||||
# Daily history for chart
|
||||
days = c.execute("SELECT date,balance FROM portfolio ORDER BY date ASC").fetchall()
|
||||
days = c.execute(f"SELECT date,balance FROM {self.tbl_portfolio} ORDER BY date ASC").fetchall()
|
||||
|
||||
conn.close()
|
||||
|
||||
@@ -230,6 +235,8 @@ class Portfolio:
|
||||
peak = INITIAL_BALANCE
|
||||
max_drawdown = 0
|
||||
for _, bal in days:
|
||||
if bal is None:
|
||||
continue
|
||||
if bal > peak:
|
||||
peak = bal
|
||||
dd = (peak - bal) / peak * 100
|
||||
@@ -261,10 +268,10 @@ class Portfolio:
|
||||
stats = self.get_stats()
|
||||
|
||||
# Get closed trades today
|
||||
today_trades = c.execute("SELECT COUNT(*),SUM(pnl_usdt) FROM trades WHERE date=? AND result IN ('WIN','LOSS')", (today,)).fetchone()
|
||||
today_trades = c.execute(f"SELECT COUNT(*),SUM(pnl_usdt) FROM {self.tbl_trades} WHERE date=? AND result IN ('WIN','LOSS')", (today,)).fetchone()
|
||||
|
||||
c.execute("""
|
||||
INSERT OR REPLACE INTO daily_snapshot
|
||||
c.execute(f"""
|
||||
INSERT OR REPLACE INTO {self.tbl_snapshot}
|
||||
(date,balance,btc_price,btc_held,open_trades,closed_trades,day_pnl,total_pnl,win_rate,created_at)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?)
|
||||
""", (
|
||||
@@ -284,12 +291,12 @@ class Portfolio:
|
||||
|
||||
|
||||
class PapertAgent:
|
||||
"""Agent executor que também atualiza portfolio simulado."""
|
||||
"""Agent executor que também atualiza portfolio simulado (Multi-Estratégia)."""
|
||||
|
||||
def __init__(self, decio_data):
|
||||
self.name = "PaperT"
|
||||
self.decio_data = decio_data
|
||||
self.portfolio = Portfolio()
|
||||
self.portfolio = Portfolio(strategy="soberana")
|
||||
self.openrouter_key = os.getenv("PAPERT_API_KEY", os.getenv("OPENROUTER_API_KEY", ""))
|
||||
self.openrouter_url = "https://openrouter.ai/api/v1/chat/completions"
|
||||
self.model = "deepseek/deepseek-v4-flash:free"
|
||||
@@ -303,10 +310,10 @@ class PapertAgent:
|
||||
break
|
||||
return True, action
|
||||
|
||||
def _format_log_entry(self, action, entry_price, stop_loss, take_profit):
|
||||
def _format_log_entry(self, action, entry_price, stop_loss, take_profit, strategy="Soberana"):
|
||||
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
fee = round(entry_price * 0.001, 2)
|
||||
entry = f"[{now}] BTC/USDT | {action} | Entry: ${entry_price:,.2f} | Fee: ${fee:.2f} | SL: ${stop_loss:,.2f} | TP: ${take_profit:,.2f}"
|
||||
entry = f"[{now}] BTC/USDT ({strategy}) | {action} | Entry: ${entry_price:,.2f} | Fee: ${fee:.2f} | SL: ${stop_loss:,.2f} | TP: ${take_profit:,.2f}"
|
||||
return entry
|
||||
|
||||
def _write_log(self, log_line):
|
||||
@@ -335,23 +342,39 @@ class PapertAgent:
|
||||
take_profit = self.decio_data.get("take_profit", 0) or 0
|
||||
amount_pct = self.decio_data.get("amount_pct", 0) or 25
|
||||
|
||||
# Execute portfolio trade
|
||||
# 1. Execute Carteira A (Soberana - Padrão)
|
||||
portfolio_result = self.portfolio._execute_trade(action, entry_price, amount_pct, stop_loss, take_profit)
|
||||
|
||||
# Log entry
|
||||
log_entry = self._format_log_entry(action, entry_price, stop_loss, take_profit)
|
||||
log_entry = self._format_log_entry(action, entry_price, stop_loss, take_profit, "Soberana")
|
||||
logged = self._write_log(log_entry)
|
||||
|
||||
# Save daily snapshot
|
||||
try:
|
||||
self.portfolio.snapshot()
|
||||
except:
|
||||
pass
|
||||
|
||||
# Build result
|
||||
try: self.portfolio.snapshot()
|
||||
except: pass
|
||||
stats = self.portfolio.get_stats()
|
||||
fee = round(entry_price * 0.001, 2)
|
||||
|
||||
# 2. Execute Carteira B (Agressiva - ignora trava de 70% e opera sinal primário do Brief)
|
||||
brief_signal = self.decio_data.get("signal", "NEUTRO")
|
||||
agr_action = "BUY" if brief_signal == "ALTA" else "SELL" if brief_signal == "BAIXA" else "HOLD"
|
||||
pf_agr = Portfolio(strategy="agressiva")
|
||||
sl_agr = round(entry_price * 0.98, 2) if agr_action == "BUY" else round(entry_price * 1.02, 2) if agr_action == "SELL" else 0
|
||||
tp_agr = round(entry_price * 1.05, 2) if agr_action == "BUY" else round(entry_price * 0.95, 2) if agr_action == "SELL" else 0
|
||||
pf_agr._execute_trade(agr_action, entry_price, 25, sl_agr, tp_agr)
|
||||
log_agr = self._format_log_entry(agr_action, entry_price, sl_agr, tp_agr, "Agressiva")
|
||||
self._write_log(log_agr)
|
||||
try: pf_agr.snapshot()
|
||||
except: pass
|
||||
stats_agr = pf_agr.get_stats()
|
||||
|
||||
# 3. Execute Carteira C (HODL Benchmark)
|
||||
pf_hodl = Portfolio(strategy="hodl")
|
||||
cur_hodl = pf_hodl.balance
|
||||
if cur_hodl["btc_held"] == 0 and cur_hodl["balance"] > 10:
|
||||
pf_hodl._execute_trade("BUY", entry_price, 100, 0, 0)
|
||||
log_hodl = self._format_log_entry("BUY" if cur_hodl["btc_held"] == 0 else "HOLD", entry_price, 0, 0, "HODL")
|
||||
self._write_log(log_hodl)
|
||||
try: pf_hodl.snapshot()
|
||||
except: pass
|
||||
stats_hodl = pf_hodl.get_stats()
|
||||
|
||||
fee = round(entry_price * 0.001, 2)
|
||||
result_str = f"[EXEC] {datetime.now().strftime('%Y-%m-%dT%H:%M:%S')} | {action} | ${entry_price:,.0f}"
|
||||
if action in ("BUY", "SELL"):
|
||||
result_str += f" | Bal: ${stats['current_balance']:.2f} | P&L total: ${stats['total_pnl']:+.2f} ({stats['total_pnl_pct']:+.1f}%)"
|
||||
@@ -367,7 +390,7 @@ class PapertAgent:
|
||||
"fee_usdt": fee,
|
||||
"log_appended": logged,
|
||||
"result": result_str,
|
||||
# Portfolio info for dashboard
|
||||
# Portfolio info for dashboard (Enriched with Multi-Strategy)
|
||||
"portfolio": {
|
||||
"balance": stats["current_balance"],
|
||||
"total_value": stats["total_value"],
|
||||
@@ -378,7 +401,21 @@ class PapertAgent:
|
||||
"losses": stats["losses"],
|
||||
"win_rate": stats["win_rate"],
|
||||
"btc_held": stats["btc_held"],
|
||||
"btc_price": stats["btc_price"]
|
||||
"btc_price": stats["btc_price"],
|
||||
# Sub-carteiras comparativas
|
||||
"agressiva": {
|
||||
"balance": stats_agr["current_balance"],
|
||||
"total_value": stats_agr["total_value"],
|
||||
"total_pnl": stats_agr["total_pnl"],
|
||||
"total_pnl_pct": stats_agr["total_pnl_pct"],
|
||||
"win_rate": stats_agr["win_rate"]
|
||||
},
|
||||
"hodl": {
|
||||
"balance": stats_hodl["current_balance"],
|
||||
"total_value": stats_hodl["total_value"],
|
||||
"total_pnl": stats_hodl["total_pnl"],
|
||||
"total_pnl_pct": stats_hodl["total_pnl_pct"]
|
||||
}
|
||||
},
|
||||
"agent": "PaperT",
|
||||
"timestamp": datetime.now().isoformat()
|
||||
|
||||
Reference in New Issue
Block a user