Mikael bot - Guardian

SIMULADOR

Panel local • tú mandas: iniciar, pausar, detener y cerrar operaciones

Estado: Desconectado
Dale al botón de conectar sin meter API.

Conexión (Bybit API)

Recomendado: backend local para firmar y proteger tu Secret
Robot
Apagado
Bybit
Desconectado
⚠️ Seguridad: el navegador no es ideal para guardar secretos. Lo correcto es que tu backend local reciba la clave, la guarde en memoria/cifrado local y nunca la exponga.
Retiros: desactivados en tu API.

Controles

Robot de trading
El robot solo opera si tú lo arrancas
Modo: SPOT
Universo: BTC, BNB, ETH, SUPER, TON, DOGE
Objetivo: +5% (PNL real)
Escaneo: cada 5 s (sin operación)
1 trade: a la vez
Panel de análisis
Lo que el robot está haciendo ahora mismo
Ajustes de inyección
Aplica solo a la próxima operación que abra el robot.
SPOT
PERPETUOS
Los porcentajes aceptan formato “40” (=40%) o “0.40”. Los cambios se guardan y el robot los obedece.
Par actual
—
USDT libre
—
Operación (margen/notional)
—
Resultado (se mueve)
—
Costos (fees/funding)
—
Inyecciones
—

Bitácora

[Listo] Esperando conexión...

Mikael bot (modo estricto)

Mikael: —
MIKAEL • esperando
Modo estricto: solo respondo si encuentro evidencia en estado/logs/manual. Si no hay evidencia: “No tengo esa información”.

Rendimiento

Últimas operaciones y resumen por periodo
USDT libre (UNIFIED)
—
Δ cierre: —
USDT libre (mercado)
—
Entrará a operar con (%)
—
Margen inicial estimado (USDT)
—
Inyección estimada
—
Ganancias y pérdidas
—
Apertura última operación
—
Cierre última operación
—
Última operación (saldo real)
—
Día (24h)
—
Semana (7d)
—
Mes (28d)
—
3 meses (84d)
—
6 meses (168d)
—
1 año (336d)
—
Por cripto
Este panel supone endpoints en tu backend local: /api/connect, /api/start, /api/pause, /api/stop, /api/close, /api/status, /api/logs, /api/emergencyReset.
`; } async function openPerfReportOverlay(){ setPerfReportOpen(true); const body = el("perfReportBody"); const sub = el("perfReportSub"); if(body) body.innerHTML = `
Cargando…
`; if(sub) sub.textContent = "Cargando…"; try{ const out = await fetchPerfReport({ force: true }); const genAt = Number(out?.generated_at); const genTxt = Number.isFinite(genAt) ? new Date(genAt).toLocaleString() : "—"; if(sub) sub.textContent = `Generado: ${genTxt}`; if(body) body.innerHTML = buildPerfReportHtml(out, { mode: "ui" }); initAdvancedMetricsSelector(out); }catch(err){ if(body) body.innerHTML = `
No pude cargar el reporte: ${escapeHtml((err?.message ?? "Error").toString())}
`; if(sub) sub.textContent = "Error"; } } async function buildPerfReportPdfPayload(){ const out = await fetchPerfReport({ force: true }); const doc = buildPerfReportPrintDoc(out); const genAt = Number(out?.generated_at); const d = Number.isFinite(genAt) ? new Date(genAt) : new Date(); const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, "0"); const da = String(d.getDate()).padStart(2, "0"); const suggestedName = `reporte_rendimiento_${y}-${m}-${da}.pdf`; return { doc, suggestedName, genAt }; } async function openPerfReportPdfPreview(){ setPerfReportPdfOpen(true); const frame = el("perfReportPdfFrame"); const sub = el("perfReportPdfSub"); if(sub) sub.textContent = "Cargando…"; perfReportPdfDoc = null; perfReportPdfSuggestedName = null; try{ const payload = await buildPerfReportPdfPayload(); const genTxt = Number.isFinite(payload.genAt) ? new Date(payload.genAt).toLocaleString() : "—"; if(sub) sub.textContent = `Generado: ${genTxt}`; perfReportPdfDoc = payload.doc; perfReportPdfSuggestedName = payload.suggestedName; if(frame){ try{ frame.srcdoc = payload.doc; }catch(_){ try{ frame.contentWindow.document.open(); frame.contentWindow.document.write(payload.doc); frame.contentWindow.document.close(); }catch(err){ appendLog("No pude generar la vista previa ❌ " + escapeHtml((err?.message ?? "Error").toString())); } } } }catch(err){ if(sub) sub.textContent = "Error"; appendLog("No pude cargar el PDF ❌ " + escapeHtml((err?.message ?? "Error").toString())); } } async function savePerfReportPdfFromPreview(){ if(!perfReportPdfDoc){ await openPerfReportPdfPreview(); } const doc = perfReportPdfDoc; const suggestedName = perfReportPdfSuggestedName || "reporte_rendimiento.pdf"; if(!doc) return; if(window.botBridge && typeof window.botBridge.savePdf === "function"){ const res = await window.botBridge.savePdf({ html: doc, suggestedName }); if(res?.ok && res?.path){ appendLog("PDF guardado ✅ " + res.path); return; } if(res?.canceled){ appendLog("Descarga cancelada."); return; } if(res?.error){ appendLog("No pude generar el PDF ❌ " + escapeHtml(String(res.error))); return; } } const frame = el("perfReportPdfFrame"); if(frame?.contentWindow){ try{ frame.contentWindow.focus?.(); frame.contentWindow.print?.(); }catch(_){} } } el("btnPerfExportPdf")?.addEventListener("click", async (e) => { try{ e?.preventDefault?.(); e?.stopPropagation?.(); }catch(_){ } await openPerfReportOverlay(); }); el("btnPerfReportClose")?.addEventListener("click", (e) => { try{ e?.preventDefault?.(); e?.stopPropagation?.(); }catch(_){ } setPerfReportOpen(false); }); el("btnPerfReportDownload")?.addEventListener("click", async (e) => { try{ e?.preventDefault?.(); e?.stopPropagation?.(); }catch(_){ } try{ await openPerfReportPdfPreview(); }catch(err){ appendLog("No pude generar el PDF ❌ " + escapeHtml((err?.message ?? "Error").toString())); } }); el("perfReportOverlay")?.addEventListener("click", (e) => { const ov = el("perfReportOverlay"); const card = el("perfReportBody")?.closest?.(".reportCard"); const t = e?.target; if(!ov || !card) return; if(t === ov) setPerfReportOpen(false); }); el("btnPerfReportPdfClose")?.addEventListener("click", (e) => { try{ e?.preventDefault?.(); e?.stopPropagation?.(); }catch(_){ } setPerfReportPdfOpen(false); }); el("btnPerfReportPdfSave")?.addEventListener("click", async (e) => { try{ e?.preventDefault?.(); e?.stopPropagation?.(); }catch(_){ } try{ await savePerfReportPdfFromPreview(); }catch(err){ appendLog("No pude generar el PDF ❌ " + escapeHtml((err?.message ?? "Error").toString())); } }); el("perfReportPdfOverlay")?.addEventListener("click", (e) => { const ov = el("perfReportPdfOverlay"); const card = el("perfReportPdfFrame")?.closest?.(".reportCard"); const t = e?.target; if(!ov || !card) return; if(t === ov) setPerfReportPdfOpen(false); }); el("btnCryptoInsight")?.addEventListener("click", async (event) => { try{ event?.preventDefault?.(); event?.stopPropagation?.(); }catch(_){ } setInsightOpen(!insightOpen); if(insightOpen){ await refreshInsight({ forceFull: true }); } }); el("btnWhales")?.addEventListener("click", async () => { if(!whalesOpen) setWhalesOpen(true); }); el("btnWhalesPopout")?.addEventListener("click", async (e) => { try{ e?.preventDefault?.(); e?.stopPropagation?.(); }catch(_){ } await openWhalesPopout({ reason: "button" }); }); el("btnWhalesMin")?.addEventListener("click", async (e) => { try{ e?.preventDefault?.(); e?.stopPropagation?.(); }catch(_){ } const ok = await openWhalesPopout({ mode: "window", reason: "minimize" }); if(ok) setWhalesOpen(false, { reason: "minimize" }); }); el("btnWhalesClose")?.addEventListener("click", async (e) => { try{ e?.preventDefault?.(); e?.stopPropagation?.(); }catch(_){ } setWhalesOpen(false, { reason: "user" }); }); el("btnInjSettings")?.addEventListener("click", (e) => { try{ e?.preventDefault?.(); e?.stopPropagation?.(); }catch(_){ } setInjSettingsOpen(!injSettingsOpen); }); el("btnInjSettingsClose")?.addEventListener("click", (e) => { try{ e?.preventDefault?.(); e?.stopPropagation?.(); }catch(_){ } setInjSettingsOpen(false); }); el("btnInjSettingsSave")?.addEventListener("click", async (e) => { try{ e?.preventDefault?.(); e?.stopPropagation?.(); }catch(_){ } try{ const cfg = readInjectionConfigFromUi(); const out = await api("/api/prefs/injection_config","POST", cfg); if(out?.prefs) { if(lastPanelStatus && typeof lastPanelStatus === "object") lastPanelStatus.prefs = out.prefs; applyInjectionPrefsToUi(out.prefs, { force: true }); renderPlannedFrom({ usdt_available: metricCache.usdt_available ?? lastPanelStatus?.usdt_available, wallet_trade_pct: out.prefs.wallet_trade_pct }); if(lastPanelStatus) renderOpCapitalFromStatus(lastPanelStatus); } appendLog("Ajustes de inyección guardados ✅"); setInjSettingsOpen(false); }catch(err){ appendLog("No pude guardar ajustes de inyección ❌ " + ((err?.message ?? "Error").toString())); } }); el("walletPct")?.addEventListener("input", () => { const v = el("walletPct")?.value; renderPlannedFrom({ usdt_available: metricCache.usdt_available ?? lastPanelStatus?.usdt_available, wallet_trade_pct: v }); if(lastPanelStatus) renderOpCapitalFromStatus(lastPanelStatus); }); document.addEventListener("click", (e) => { if(!injSettingsOpen) return; const panel = el("injSettingsPanel"); const btn = el("btnInjSettings"); const t = e?.target; if(panel && panel.contains(t)) return; if(btn && btn.contains(t)) return; setInjSettingsOpen(false); }); document.addEventListener("click", (e) => { if(!insightOpen) return; const panel = el("cryptoInsightPanel"); const btn = el("btnCryptoInsight"); const t = e?.target; if(panel && panel.contains(t)) return; if(btn && btn.contains(t)) return; setInsightOpen(false); }); el("selSymbol")?.addEventListener("change", () => { refreshWhalesPulse({ force: true }); }); el("selMarket")?.addEventListener("change", () => { refreshWhalesPulse({ force: true }); }); el("btnCryptoInsightClose")?.addEventListener("click", () => { setInsightOpen(false, { restoreFocus: true }); }); document.addEventListener("keydown", (event) => { if(event.key === "Escape" && insightOpen) setInsightOpen(false, { restoreFocus: true }); }); initSoundSettings(); detectBackendBaseUrl(); scheduleRightPanelHeightSync(); setTimeout(scheduleRightPanelHeightSync, 80); setTimeout(scheduleRightPanelHeightSync, 320); window.addEventListener("resize", scheduleRightPanelHeightSync); window.addEventListener("load", () => { scheduleRightPanelHeightSync(); setTimeout(scheduleRightPanelHeightSync, 80); }); try{ const left = el("leftPanel"); if(left && typeof ResizeObserver !== "undefined"){ const ro = new ResizeObserver(() => scheduleRightPanelHeightSync()); ro.observe(left); } }catch(_){ } mountWhalesDrag(); setStatus("disconnected","Detectando estado…"); setControls({connected:false, running:false, paused:false}); startPolling();