Files
DOLPHIN/Observability/dolphin_status_v1.py

186 lines
6.3 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""DOLPHIN live status — zero-dependency rolling display.
Polls HZ every 5s, prints a compact status block. No curses, no textual.
Run: source /home/dolphin/siloqy_env/bin/activate && python dolphin_status.py
"""
import json, os, time, sys
from datetime import datetime, timezone
import hazelcast
CLEAR = "\033[2J\033[H"
BOLD = "\033[1m"; DIM = "\033[2m"; RST = "\033[0m"
GREEN = "\033[32m"; YELLOW = "\033[33m"; RED = "\033[31m"; CYAN = "\033[36m"
PC = {"APEX": GREEN, "STALKER": YELLOW, "TURTLE": "\033[38;5;208m", "HIBERNATE": RED}
SC = {"GREEN": GREEN, "DEGRADED": YELLOW, "CRITICAL": "\033[38;5;208m", "DEAD": RED}
START_CAP = None
CAP_PEAK = None
def _age(ts):
if not ts: return "?"
s = time.time() - ts
if s < 0: return "0s"
if s < 60: return f"{s:.0f}s"
if s < 3600: return f"{s/60:.0f}m"
return f"{s/3600:.1f}h"
def _bar(v, w=20):
v = max(0.0, min(1.0, v))
f = round(v * w)
return "" * f + "" * (w - f)
def _get(hz, map_name, key):
try:
raw = hz.get_map(map_name).blocking().get(key)
return json.loads(raw) if raw else {}
except Exception:
return {}
def render(hz):
global START_CAP, CAP_PEAK
eng = _get(hz, "DOLPHIN_STATE_BLUE", "engine_snapshot")
cap = _get(hz, "DOLPHIN_STATE_BLUE", "capital_checkpoint")
safe = _get(hz, "DOLPHIN_SAFETY", "latest")
hb = _get(hz, "DOLPHIN_HEARTBEAT", "nautilus_flow_heartbeat")
mh = _get(hz, "DOLPHIN_META_HEALTH", "latest")
acb = _get(hz, "DOLPHIN_FEATURES", "acb_boost")
now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
capital = float(eng.get("capital", 0) or cap.get("capital", 0))
posture = safe.get("posture") or eng.get("posture") or "?"
rm = float(safe.get("Rm", 0))
bd = safe.get("breakdown", {})
hb_ts = hb.get("ts")
hb_age = _age(hb_ts)
phase = hb.get("phase", "?")
trader_up = hb_ts and (time.time() - hb_ts) < 30
trades = eng.get("trades_executed", "")
scans = eng.get("scans_processed", "")
lev = float(eng.get("current_leverage", 0))
notional = float(eng.get("open_notional", 0))
mhs_st = mh.get("status", "?")
rm_meta = float(mh.get("rm_meta", 0))
boost = float(acb.get("boost", acb.get("cut", 0)))
vel_div = float(eng.get("last_vel_div", 0))
if capital > 0:
if START_CAP is None:
START_CAP = capital
if CAP_PEAK is None or capital > CAP_PEAK:
CAP_PEAK = capital
roi = ((capital - START_CAP) / START_CAP * 100) if START_CAP and capital else 0
dd = ((CAP_PEAK - capital) / CAP_PEAK * 100) if CAP_PEAK and capital < CAP_PEAK else 0
pc = PC.get(posture, DIM)
sc = SC.get(mhs_st, DIM)
tc = GREEN if trader_up else RED
roi_c = GREEN if roi >= 0 else RED
dd_c = RED if dd > 15 else (YELLOW if dd > 5 else GREEN)
svc = mh.get("service_status", {})
hz_ks = mh.get("hz_key_status", {})
lines = []
lines.append(f"{BOLD}{CYAN}🐬 DOLPHIN-NAUTILUS{RST} {DIM}{now}{RST}")
lines.append(f"{'' * 60}")
# TRADER
lines.append(f"{BOLD}TRADER{RST} {tc}{'● LIVE' if trader_up else '● DOWN'}{RST}"
f" phase:{phase} hb:{hb_age}")
lines.append(f" vel_div:{vel_div:+.5f} scan:#{eng.get('last_scan_number', '?')}")
lines.append("")
# CAPITAL
lines.append(f"{BOLD}CAPITAL{RST} {CYAN}${capital:,.2f}{RST}")
lines.append(f" ROI: {roi_c}{roi:+.2f}%{RST} DD: {dd_c}{dd:.2f}%{RST}"
f" start: ${START_CAP:,.0f}" if START_CAP else "")
lines.append(f" trades:{trades} scans:{scans} bar:{eng.get('bar_idx', '?')}")
lines.append(f" lev:{lev:.2f}x notional:${notional:,.0f}")
# Open positions
positions = eng.get("open_positions", [])
if positions:
lines.append(f" {BOLD}OPEN POSITIONS:{RST}")
for p in positions:
side_c = GREEN if p.get("side") == "LONG" else RED
lines.append(f" {side_c}{p.get('asset','?')} {p.get('side','?')}{RST}"
f" qty:{p.get('quantity',0):.4f}"
f" entry:{p.get('entry_price',0):.2f}"
f" pnl:{p.get('unrealized_pnl',0):+.2f}")
else:
lines.append(f" {DIM}no open positions{RST}")
lines.append("")
# POSTURE
lines.append(f"{BOLD}POSTURE{RST} {pc}{posture}{RST} Rm:{pc}{_bar(rm, 20)}{RST} {rm:.4f}")
cats = " ".join(f"C{i}:{float(bd.get(f'Cat{i}', 0)):.2f}" for i in range(1, 6))
lines.append(f" {cats}")
lines.append(f" f_env:{float(bd.get('f_env', 0)):.3f} f_exe:{float(bd.get('f_exe', 0)):.3f}"
f" boost:{boost:.2f}")
lines.append("")
# SYS HEALTH
lines.append(f"{BOLD}SYS HEALTH{RST} {sc}{mhs_st}{RST} rm_meta:{rm_meta:.3f}")
for m in ("m1_data_infra", "m1_trader", "m2_heartbeat",
"m3_data_freshness", "m4_control_plane", "m5_coherence"):
v = float(mh.get(m, 0))
c = GREEN if v >= 0.9 else (YELLOW if v >= 0.5 else RED)
lines.append(f" {m}: {c}{v:.3f}{RST}")
# Services
lines.append(f" {DIM}services:{RST}")
for name, st in sorted(svc.items()):
dot = f"{GREEN}{RST}" if st == "RUNNING" else f"{RED}{RST}"
lines.append(f" {dot} {name}")
# HZ keys
lines.append(f" {DIM}hz keys:{RST}")
for name, info in sorted(hz_ks.items()):
score = float(info.get("score", 0))
c = GREEN if score >= 0.9 else (YELLOW if score >= 0.5 else RED)
lines.append(f" {c}{RST} {name}: {info.get('status', '?')}")
lines.append(f"\n{'' * 60}")
lines.append(f"{DIM}polling 5s • q to quit • {now}{RST}")
return "\n".join(lines)
def main():
print("Connecting to HZ...")
hz = hazelcast.HazelcastClient(
cluster_name="dolphin", cluster_members=["localhost:5701"],
connection_timeout=5.0)
print("Connected. Starting status display...\n")
try:
while True:
try:
out = render(hz)
sys.stdout.write(CLEAR + out + "\n")
sys.stdout.flush()
except Exception as e:
sys.stdout.write(f"\n{RED}Render error: {e}{RST}\n")
sys.stdout.flush()
time.sleep(5)
except KeyboardInterrupt:
print(f"\n{DIM}Bye.{RST}")
finally:
hz.shutdown()
if __name__ == "__main__":
main()