#!/usr/bin/env python3 """ DOLPHIN TUI v4 ============== Fixes vs v3: • SYS HEALTH: tdr/scb labels expanded to full service names • ALPHA ENGINE: falls back to engine_snapshot when DOLPHIN_SAFETY is empty • MC-FOREWARNER: graceful "not yet run" display; shows last-run age; no crash on absent key • Version number shown in header after MC status Run: source /home/dolphin/siloqy_env/bin/activate && python dolphin_tui_v4.py Keys: q=quit r=force-refresh l=toggle log t=toggle test footer """ # ── stdlib ──────────────────────────────────────────────────────────────────── import asyncio, json, math, threading, time from collections import deque from datetime import datetime, timezone from pathlib import Path from typing import Any, Dict, Optional # ── third-party ─────────────────────────────────────────────────────────────── try: from textual.app import App, ComposeResult from textual.containers import Horizontal, Vertical from textual.widgets import Digits, ProgressBar, Sparkline, Static except ImportError as e: raise SystemExit(f"textual not found — activate siloqy_env: {e}") try: import hazelcast _HZ_OK = True except ImportError: _HZ_OK = False TUI_VERSION = "v4" _META_JSON = Path("/mnt/dolphinng5_predict/run_logs/meta_health.json") _CAPITAL_JSON = Path("/tmp/dolphin_capital_checkpoint.json") _TEST_JSON = Path("/mnt/dolphinng5_predict/run_logs/test_results_latest.json") _OBF_SYMS = ["BTCUSDT", "ETHUSDT", "SOLUSDT", "BNBUSDT", "XRPUSDT"] _PC = {"APEX":"green","STALKER":"yellow","TURTLE":"dark_orange","HIBERNATE":"red"} _MC = {"GREEN":"green","ORANGE":"dark_orange","RED":"red"} _SC = {"GREEN":"green","DEGRADED":"yellow","CRITICAL":"dark_orange","DEAD":"red"} # ── Thread-safe state ───────────────────────────────────────────────────────── class _State: def __init__(self): self._l = threading.Lock(); self._d: Dict[str, Any] = {} def put(self, k, v): with self._l: self._d[k] = v def get(self, k, default=None): with self._l: return self._d.get(k, default) def update(self, m): with self._l: self._d.update(m) _S = _State() def _ingest(key, raw): if not raw: return try: _S.update({f"hz.{key}": json.loads(raw), f"hz.{key}._t": time.time()}) except Exception: pass def start_hz_listener(on_scan=None): if not _HZ_OK: _S.put("hz_up", False); return def _run(): while True: try: _S.put("hz_up", False) client = hazelcast.HazelcastClient( cluster_name="dolphin", cluster_members=["localhost:5701"], connection_timeout=5.0) _S.put("hz_up", True) fm = client.get_map("DOLPHIN_FEATURES").blocking() for k in ("latest_eigen_scan","exf_latest","acb_boost", "obf_universe_latest","mc_forewarner_latest"): _ingest(k, fm.get(k)) def _f(e): _ingest(e.key, e.value) if e.key == "latest_eigen_scan" and on_scan: try: on_scan() except Exception: pass fm.add_entry_listener(include_value=True, updated=_f, added=_f) for map_name, key, state_key in [ ("DOLPHIN_META_HEALTH", "latest", "meta_health"), ("DOLPHIN_SAFETY", "latest", "safety"), ("DOLPHIN_HEARTBEAT", "nautilus_flow_heartbeat", "heartbeat"), ]: m = client.get_map(map_name).blocking() _ingest(state_key, m.get(key)) def _cb(e, sk=state_key, ek=key): if e.key == ek: _ingest(sk, e.value) m.add_entry_listener(include_value=True, updated=_cb, added=_cb) stm = client.get_map("DOLPHIN_STATE_BLUE").blocking() _ingest("capital", stm.get("capital_checkpoint")) _ingest("engine_snapshot", stm.get("engine_snapshot")) def _cap(e): if e.key == "capital_checkpoint": _ingest("capital", e.value) elif e.key == "engine_snapshot": _ingest("engine_snapshot", e.value) stm.add_entry_listener(include_value=True, updated=_cap, added=_cap) try: pm = client.get_map("DOLPHIN_PNL_BLUE").blocking() _ingest("pnl_blue", pm.get("session_perf")) def _pnl(e): if e.key == "session_perf": _ingest("pnl_blue", e.value) pm.add_entry_listener(include_value=True, updated=_pnl, added=_pnl) except Exception: pass while True: time.sleep(5) if not getattr(client.lifecycle_service, "is_running", lambda: True)(): break except Exception as e: _S.put("hz_up", False); _S.put("hz_err", str(e)); time.sleep(10) threading.Thread(target=_run, daemon=True, name="hz-listener").start() async def prefect_poll_loop(): while True: try: from prefect.client.orchestration import get_client from prefect.client.schemas.sorting import FlowRunSort async with get_client() as pc: runs = await pc.read_flow_runs(limit=20, sort=FlowRunSort.START_TIME_DESC) seen: Dict[str, Any] = {} fid_to_name: Dict[str, str] = {} for r in runs: fid = str(r.flow_id) if fid not in seen: seen[fid] = r rows = [] for fid, r in seen.items(): if fid not in fid_to_name: try: f = await pc.read_flow(r.flow_id) fid_to_name[fid] = f.name except Exception: fid_to_name[fid] = fid[:8] rows.append({"name": fid_to_name[fid], "state": r.state_name or "?", "ts": r.start_time.strftime("%m-%d %H:%M") if r.start_time else "--"}) _S.put("prefect_flows", rows[:8]); _S.put("prefect_ok", True) except Exception as e: _S.put("prefect_ok", False); _S.put("prefect_err", str(e)[:60]) await asyncio.sleep(60) # ── Helpers ─────────────────────────────────────────────────────────────────── 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 _age_col(ts, warn=15, dead=60): s = time.time() - ts if ts else 9999 return "red" if s > dead else ("yellow" if s > warn else "green") def _bar(v, width=12): v = max(0.0, min(1.0, v)) f = round(v * width) return "█" * f + "░" * (width - f) def _fmt_vel(v): return "---" if v is None else f"{float(v):+.5f}" def _dot(state): s = (state or "").upper() if s == "COMPLETED": return "[green]●[/green]" if s == "RUNNING": return "[cyan]●[/cyan]" if s in ("FAILED","CRASHED","TIMEDOUT"): return "[red]●[/red]" if s == "CANCELLED": return "[dim]●[/dim]" if s == "PENDING": return "[yellow]●[/yellow]" return "[dim]◌[/dim]" def _posture_markup(p): c = _PC.get(p, "dim") return f"[{c}]{p}[/{c}]" def _col(v, c): return f"[{c}]{v}[/{c}]" def _eigen_from_scan(scan): if not scan: return {} r = scan.get("result", scan) mwr_raw = r.get("multi_window_results", {}) def _td(w): return (mwr_raw.get(w) or mwr_raw.get(str(w)) or {}).get("tracking_data", {}) def _rs(w): return (mwr_raw.get(w) or mwr_raw.get(str(w)) or {}).get("regime_signals", {}) v50 = _td(50).get("lambda_max_velocity") or scan.get("w50_velocity", 0.0) v150 = _td(150).get("lambda_max_velocity") or scan.get("w150_velocity", 0.0) v300 = _td(300).get("lambda_max_velocity") or scan.get("w300_velocity", 0.0) v750 = _td(750).get("lambda_max_velocity") or scan.get("w750_velocity", 0.0) vel_div = scan.get("vel_div", float(v50 or 0) - float(v150 or 0)) inst_avg = sum(_rs(w).get("instability_score", 0.0) for w in (50,150,300,750)) / 4 bt_price = (r.get("pricing_data", {}) or {}).get("current_prices", {}).get("BTCUSDT") return { "scan_number": scan.get("scan_number", 0), "timestamp": scan.get("timestamp", 0), "vel_div": float(vel_div or 0), "v50": float(v50 or 0), "v150": float(v150 or 0), "v300": float(v300 or 0), "v750": float(v750 or 0), "inst_avg": float(inst_avg or 0), "btc_price": float(bt_price) if bt_price else None, "regime": r.get("regime", r.get("sentiment", "?")), "version": scan.get("version", "?"), } # ── CSS ─────────────────────────────────────────────────────────────────────── _CSS = """ Screen { background: #0a0a0a; color: #d4d4d4; } #header { height: 2; background: #111; border-bottom: solid #333; padding: 0 1; } #trader_row { height: 5; } #top_row { height: 9; } #mid_row { height: 9; } #bot_row { height: 7; } #log_row { height: 5; display: none; } #mc_outer { height: 16; border: solid #224; background: #060616; } #mc_title { height: 1; padding: 0 1; } #mc_body { height: 15; } #mc_left { width: 18; padding: 0 1; } #mc_center { width: 1fr; padding: 0 1; } #mc_right { width: 30; padding: 0 1; } #mc_prob_label { height: 1; } #mc_prob_bar { height: 1; } #mc_env_label { height: 1; } #mc_env_bar { height: 1; } #mc_champ_label{ height: 1; } #mc_champ_bar { height: 1; } #mc_live { height: 8; } #mc_spark_lbl { height: 1; } #mc_spark { height: 2; } #mc_mae_lbl { height: 1; } #mc_mae_spark { height: 2; } #mc_digits { height: 3; } #mc_status { height: 3; } #mc_legend { height: 6; } #test_footer { height: 3; background: #101010; border-top: solid #2a2a2a; padding: 0 1; } Static.panel { border: solid #333; padding: 0 1; height: 100%; } #p_trader { width: 1fr; border: solid #006650; } #p_health { width: 1fr; } #p_alpha { width: 1fr; } #p_scan { width: 1fr; } #p_extf { width: 1fr; } #p_obf { width: 1fr; } #p_capital { width: 1fr; } #p_prefect { width: 1fr; } #p_acb { width: 1fr; } #p_log { width: 1fr; border: solid #333; padding: 0 1; } ProgressBar > .bar--bar { color: $success; } ProgressBar > .bar--complete { color: $success; } ProgressBar.-danger > .bar--bar { color: $error; } ProgressBar.-warning > .bar--bar { color: $warning; } """ # ── App ─────────────────────────────────────────────────────────────────────── class DolphinTUI(App): CSS = _CSS BINDINGS = [("q","quit","Quit"),("r","force_refresh","Refresh"), ("l","toggle_log","Log"),("t","toggle_tests","Tests")] _log_vis = False; _test_vis = True _prob_hist: deque; _mae_deque: deque _session_start_cap: Optional[float] = None _cap_peak: Optional[float] = None def compose(self) -> ComposeResult: yield Static("", id="header") with Horizontal(id="trader_row"): yield Static("", classes="panel", id="p_trader") with Horizontal(id="top_row"): yield Static("", classes="panel", id="p_health") yield Static("", classes="panel", id="p_alpha") yield Static("", classes="panel", id="p_scan") with Horizontal(id="mid_row"): yield Static("", classes="panel", id="p_extf") yield Static("", classes="panel", id="p_obf") yield Static("", classes="panel", id="p_capital") with Horizontal(id="bot_row"): yield Static("", classes="panel", id="p_prefect") yield Static("", classes="panel", id="p_acb") with Vertical(id="mc_outer"): yield Static("", id="mc_title") with Horizontal(id="mc_body"): with Vertical(id="mc_left"): yield Digits("0.000", id="mc_digits") yield Static("", id="mc_status") with Vertical(id="mc_center"): yield Static("", id="mc_prob_label") yield ProgressBar(total=100, show_eta=False, show_percentage=False, id="mc_prob_bar") yield Static("", id="mc_env_label") yield ProgressBar(total=100, show_eta=False, show_percentage=False, id="mc_env_bar") yield Static("", id="mc_champ_label") yield ProgressBar(total=100, show_eta=False, show_percentage=False, id="mc_champ_bar") yield Static("", id="mc_live") with Vertical(id="mc_right"): yield Static("", id="mc_spark_lbl") yield Sparkline([], id="mc_spark") yield Static("", id="mc_mae_lbl") yield Sparkline([], id="mc_mae_spark") yield Static("", id="mc_legend") yield Static("", id="test_footer") with Horizontal(id="log_row"): yield Static("", classes="panel", id="p_log") def on_mount(self) -> None: self._prob_hist = deque([0.0] * 40, maxlen=40) self._mae_deque = deque(maxlen=500) start_hz_listener(on_scan=lambda: self.call_from_thread(self._update)) self.run_worker(prefect_poll_loop(), name="prefect-poll", exclusive=True) self.set_interval(1.0, self._update) self._update() def _update(self) -> None: now_str = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC") hz_up = _S.get("hz_up", False) mh = _S.get("hz.meta_health") or self._read_json(_META_JSON) safe = _S.get("hz.safety") or {} scan = _S.get("hz.latest_eigen_scan") or {} exf = _S.get("hz.exf_latest") or {} acb = _S.get("hz.acb_boost") or {} obf_u = _S.get("hz.obf_universe_latest") or {} mc = _S.get("hz.mc_forewarner_latest") or {} cap = _S.get("hz.capital") or self._read_json(_CAPITAL_JSON) or {} hb = _S.get("hz.heartbeat") or {} eng = _S.get("hz.engine_snapshot") or {} eigen = _eigen_from_scan(scan) # ── posture: DOLPHIN_SAFETY first, fall back to engine_snapshot ─────── posture = safe.get("posture") or eng.get("posture") or "?" # ── Rm / breakdown: DOLPHIN_SAFETY first, fall back to zeros ───────── rm_s = float(safe.get("Rm", 0.0)) bd = safe.get("breakdown") or {} # If DOLPHIN_SAFETY is empty but engine_snapshot has posture, show that safety_live = bool(safe.get("posture") or safe.get("Rm")) rm_m = mh.get("rm_meta", 0.0) if mh else 0.0 mhs_st = mh.get("status", "?") if mh else "?" sc_mhs = _SC.get(mhs_st, "dim") pc_col = _PC.get(posture, "dim") hz_tag = "[green][HZ✓][/green]" if hz_up else "[red][HZ✗][/red]" mc_st = mc.get("status", "N/A") if mc else "N/A" mc_col = _MC.get(mc_st, "dim") # ── HEADER ──────────────────────────────────────────────────────────── self._w("#header").update( f"[bold cyan]🐬 DOLPHIN-NAUTILUS[/bold cyan] {now_str}" f" {hz_tag} [{sc_mhs}]MHS:{mhs_st} {rm_m:.3f}[/{sc_mhs}]" f" [{pc_col}]◈{posture}[/{pc_col}]" f" [{mc_col}]MC:{mc_st}[/{mc_col}]" f" [dim]{TUI_VERSION}[/dim]\n" f"[dim] localhost:5701 q=quit r=refresh l=log t=tests[/dim]" ) # ── TRADER ──────────────────────────────────────────────────────────── cap_val = float(cap.get("capital", 0)) if cap else 0.0 hb_phase = hb.get("phase", "?") if hb else "N/A" hb_ts = hb.get("ts") if hb else None hb_age = _age(hb_ts) if hb_ts else "?" hb_col = _age_col(hb_ts, 30, 120) if hb_ts else "red" vel_div = eigen.get("vel_div", 0.0) vc = "green" if vel_div > 0 else ("red" if vel_div < -0.02 else "yellow") scan_no = eigen.get("scan_number", 0) btc_p = eigen.get("btc_price") btc_str = f"BTC:[cyan]${btc_p:,.0f}[/cyan] " if btc_p else "" trades_ex= eng.get("trades_executed") last_vd = eng.get("last_vel_div") self._w("#p_trader").update( f"[bold cyan]TRADER[/bold cyan] {_posture_markup(posture)}" f" phase:[{hb_col}]{hb_phase}[/{hb_col}] hb:{_col(hb_age, hb_col)}" f" scan:[dim]#{scan_no}[/dim] {btc_str}\n" f" vel_div:[{vc}]{vel_div:+.5f}[/{vc}] thr:[dim]-0.02000[/dim]" f" cap:[cyan]${cap_val:,.0f}[/cyan]" f" trades:{trades_ex if trades_ex is not None else '—'}\n" f" last_vel_div:[dim]{last_vd:+.5f}[/dim] open-pos:[dim]DOLPHIN_PNL_BLUE[/dim]" if last_vd is not None else f" vel_div:[{vc}]{vel_div:+.5f}[/{vc}] thr:[dim]-0.02000[/dim]" f" cap:[cyan]${cap_val:,.0f}[/cyan]" f" trades:{trades_ex if trades_ex is not None else '—'}\n" f" open-pos:[dim]DOLPHIN_PNL_BLUE (not yet wired)[/dim]" ) # ── SYS HEALTH — FIX: expand tdr/scb labels ────────────────────────── if mh: svc = mh.get("service_status", {}) hz_ks= mh.get("hz_key_status", {}) def _svc(nm, label): st = svc.get(nm, "?") dot = "[green]●[/green]" if st == "RUNNING" else "[red]●[/red]" return f"{dot}[dim]{label}[/dim]" def _hz_dot(nm): sc = hz_ks.get(nm, {}).get("score", 0) return "[green]●[/green]" if sc >= 0.9 else ("[yellow]●[/yellow]" if sc >= 0.5 else "[red]●[/red]") self._w("#p_health").update( f"[bold]SYS HEALTH[/bold] [{sc_mhs}]{mhs_st}[/{sc_mhs}]\n" f"rm:[{sc_mhs}]{rm_m:.3f}[/{sc_mhs}]" f" m4:{mh.get('m4_control_plane',0):.2f}" f" m3:{mh.get('m3_data_freshness',0):.2f}" f" m5:{mh.get('m5_coherence',0):.2f}\n" f"{_svc('dolphin_data:exf_fetcher','exf')}" f" {_svc('dolphin_data:acb_processor','acb')}" f" {_svc('dolphin_data:obf_universe','obf')}\n" f"{_svc('dolphin:nautilus_trader','trader')}" f" {_svc('dolphin:scan_bridge','scan-bridge')}\n" f"[dim]hz: exf{_hz_dot('exf_latest')}" f" scan{_hz_dot('latest_eigen_scan')}" f" obf{_hz_dot('obf_universe')}[/dim]" ) else: self._w("#p_health").update("[bold]SYS HEALTH[/bold]\n[dim]awaiting MHS…[/dim]") # ── ALPHA ENGINE — FIX: fall back to engine_snapshot when safety empty ─ safe_ts = _S.get("hz.safety._t") safe_age = _age(safe_ts) if safe_ts else "?" safe_ac = _age_col(safe_ts, 30, 120) if safe_ts else "red" def _cat(n): v = bd.get(f"Cat{n}", 0.0) c = "green" if v >= 0.9 else ("yellow" if v >= 0.6 else "red") return f"[{c}]{v:.2f}[/{c}]" if safety_live: self._w("#p_alpha").update( f"[bold]ALPHA ENGINE[/bold] {_posture_markup(posture)}\n" f"Rm:[{pc_col}]{_bar(rm_s,14)}[/{pc_col}]{rm_s:.3f}\n" f"C1:{_cat(1)} C2:{_cat(2)} C3:{_cat(3)}\n" f"C4:{_cat(4)} C5:{_cat(5)}" f" fenv:{bd.get('f_env',0):.2f} fex:{bd.get('f_exe',0):.2f}\n" f"[dim]age:{_col(safe_age, safe_ac)}[/dim]" ) else: # DOLPHIN_SAFETY empty — show what we have from engine_snapshot bars_idx = eng.get("bar_idx", "?") scans_p = eng.get("scans_processed", "?") self._w("#p_alpha").update( f"[bold]ALPHA ENGINE[/bold] {_posture_markup(posture)}\n" f"[yellow]DOLPHIN_SAFETY empty[/yellow]\n" f"posture from engine_snapshot\n" f"bar:{bars_idx} scans:{scans_p}\n" f"[dim]Rm/Cat1-5: awaiting DOLPHIN_SAFETY[/dim]" ) # ── SCAN ────────────────────────────────────────────────────────────── scan_ac = _age_col(eigen.get("timestamp", 0), 15, 60) scan_age= _age(eigen.get("timestamp")) if eigen.get("timestamp") else "?" vi = eigen.get("inst_avg", 0) self._w("#p_scan").update( f"[bold]SCAN {eigen.get('version','?')}[/bold]" f" [dim]#{scan_no}[/dim] age:[{scan_ac}]{scan_age}[/{scan_ac}]\n" f"vel_div:[{vc}]{vel_div:+.5f}[/{vc}]\n" f"w50:[yellow]{_fmt_vel(eigen.get('v50'))}[/yellow]" f" w150:[dim]{_fmt_vel(eigen.get('v150'))}[/dim]\n" f"w300:[dim]{_fmt_vel(eigen.get('v300'))}[/dim]" f" w750:[dim]{_fmt_vel(eigen.get('v750'))}[/dim]\n" f"inst:{vi:.4f}" ) # ── ExtF ────────────────────────────────────────────────────────────── exf_t = _S.get("hz.exf_latest._t") exf_age = _age(exf_t) if exf_t else "?" exf_ac = _age_col(exf_t, 30, 120) if exf_t else "red" f_btc = exf.get("funding_btc"); dvol = exf.get("dvol_btc") fng = exf.get("fng"); taker= exf.get("taker") ls_btc = exf.get("ls_btc"); vix = exf.get("vix") ok_cnt = exf.get("_ok_count", 0) dvol_c = "red" if dvol and dvol > 70 else ("yellow" if dvol and dvol > 50 else "green") fng_c = "red" if fng and fng < 25 else ("yellow" if fng and fng < 45 else "green") if exf: self._w("#p_extf").update( f"[bold]ExtF[/bold] [{exf_ac}]{ok_cnt}/9 {exf_age}[/{exf_ac}]\n" f"fund:[cyan]{f_btc:.5f}[/cyan] dvol:[{dvol_c}]{dvol:.1f}[/{dvol_c}]\n" f"fng:[{fng_c}]{int(fng) if fng else '?'}[/{fng_c}] taker:[yellow]{taker:.3f}[/yellow]\n" f"ls_btc:{ls_btc:.3f} vix:{vix:.1f}\n" f"acb✓:{exf.get('_acb_ready','?')}" ) else: self._w("#p_extf").update("[bold]ExtF[/bold]\n[dim]no data[/dim]") # ── OBF ─────────────────────────────────────────────────────────────── obf_t = _S.get("hz.obf_universe_latest._t") obf_age = _age(obf_t) if obf_t else "?" obf_ac = _age_col(obf_t, 30, 120) if obf_t else "red" n_assets= obf_u.get("_n_assets", 0) if obf_u else 0 lines = [f"[bold]OBF[/bold] [{obf_ac}]n={n_assets} {obf_age}[/{obf_ac}]"] for sym in _OBF_SYMS: if not obf_u: break a = obf_u.get(sym) if not a: continue imb = float(a.get("imbalance", 0)) fp = float(a.get("fill_probability", 0)) dq = float(a.get("depth_quality", 0)) imb_c = "green" if imb > 0.1 else ("red" if imb < -0.1 else "yellow") lines.append(f"{sym[:3]} [{imb_c}]{imb:+.2f}[/{imb_c}] fp:{fp:.2f} dq:{dq:.2f}") self._w("#p_obf").update("\n".join(lines[:6])) # ── CAPITAL ─────────────────────────────────────────────────────────── cap_t = _S.get("hz.capital._t") cap_ac = _age_col(cap_t, 60, 300) if cap_t else "dim" cap_age= _age(cap_t) if cap_t else "?" c5 = bd.get("Cat5", 1.0) if bd else 1.0 try: dd_est = 0.12 + math.log(1.0/c5 - 1.0) / 30.0 if 0 < c5 < 1 else 0.0 except Exception: dd_est = 0.0 dd_c = "red" if dd_est > 0.15 else ("yellow" if dd_est > 0.08 else "green") self._w("#p_capital").update( f"[bold]CAPITAL[/bold] [{cap_ac}]{cap_age}[/{cap_ac}]\n" f"Cap:[cyan]${cap_val:,.0f}[/cyan]\n" f"DD≈:[{dd_c}]{dd_est*100:.1f}%[/{dd_c}] C5:{c5:.3f}\n" f"Pos:{_posture_markup(posture)}\n" f"[dim]pnl/trades: DOLPHIN_PNL_BLUE[/dim]" ) # ── PREFECT ─────────────────────────────────────────────────────────── flows = _S.get("prefect_flows") or [] pf_ok = _S.get("prefect_ok", False) pf_hdr = "[green]✓[/green]" if pf_ok else "[red]✗[/red]" flines = "\n".join( f"{_dot(f['state'])} {f['name'][:22]:<22} {f['ts']}" for f in flows[:5]) self._w("#p_prefect").update( f"[bold]PREFECT[/bold] {pf_hdr}\n" + (flines or "[dim]polling…[/dim]")) # ── ACB ─────────────────────────────────────────────────────────────── acb_t = _S.get("hz.acb_boost._t") acb_age = _age(acb_t) if acb_t else "?" acb_ac = _age_col(acb_t, 3600, 86400) if acb_t else "dim" boost = acb.get("boost", 1.0) if acb else 1.0 beta = acb.get("beta", 0.8) if acb else 0.8 cut = acb.get("cut", 0.0) if acb else 0.0 boost_c = "green" if boost >= 1.5 else ("yellow" if boost >= 1.0 else "red") cut_c = "red" if cut > 0 else "dim" self._w("#p_acb").update( f"[bold]ACB[/bold] [{acb_ac}]{acb.get('date','?') if acb else '?'}[/{acb_ac}]\n" f"boost:[{boost_c}]{boost:.2f}x[/{boost_c}] β={beta:.2f}" f" cut:[{cut_c}]{cut:.2f}[/{cut_c}]\n" f"w750_thr:{acb.get('w750_threshold',0.0) if acb else 0.0:.4f}\n" f"[dim]dvol={acb.get('factors',{}).get('dvol_btc','?') if acb else '?'}" f" fng={acb.get('factors',{}).get('fng','?') if acb else '?'}[/dim]\n" f"[dim]age:{acb_age} cfg:{acb.get('config_used','?') if acb else '?'}[/dim]" ) # ── MC-FOREWARNER — FIX: graceful when absent ───────────────────────── prob = float(mc.get("catastrophic_prob", 0.0)) if mc else 0.0 env = float(mc.get("envelope_score", 0.0)) if mc else 0.0 champ_p = mc.get("champion_probability") if mc else None mc_ts = mc.get("timestamp") if mc else None mc_warns = mc.get("warnings", []) if mc else [] sc = _MC.get(mc_st, "dim") self._prob_hist.append(prob) # Age since last 4h run mc_age_str = "never run" if mc_ts: try: mc_dt = datetime.fromisoformat(mc_ts.replace("Z", "+00:00")) age_s = (datetime.now(timezone.utc) - mc_dt).total_seconds() age_m = int(age_s // 60) mc_age_str = f"{age_m//60}h{age_m%60:02d}m ago" if age_m >= 60 else f"{age_m}m ago" except Exception: pass mc_present = bool(mc) self._w("#mc_title").update( f"[bold cyan]⚡ MC-FOREWARNER RISK MANIFOLD[/bold cyan] {TUI_VERSION}" + (f" [{sc}]▶ {mc_st}[/{sc}] [dim]assessed:{mc_age_str} cadence:4h[/dim]" if mc_present else " [yellow]⚠ no data yet — Prefect 4h schedule not yet run[/yellow]" " [dim](mc_forewarner_flow runs every 4h)[/dim]") ) # Left: digits + status self._w("#mc_digits", Digits).update(f"{prob:.3f}") status_str = {"GREEN":"🟢 SAFE","ORANGE":"🟡 CAUTION","RED":"🔴 DANGER"}.get(mc_st, "⚪ N/A") champ_str = f"champ:{champ_p*100:.0f}%" if champ_p is not None else "champ:—" self._w("#mc_status").update( (f"[{sc}]{status_str}[/{sc}]\n[dim]cat.prob {champ_str}[/dim]\n[dim]env:{env:.3f}[/dim]") if mc_present else "[yellow]awaiting[/yellow]\n[dim]first run[/dim]\n[dim]in ~4h[/dim]" ) # Center bars for bar_id, val, lo_thr, hi_thr, lo_cls, hi_cls, label, fmt in [ ("mc_prob_bar", prob, 0.10, 0.30, "-warning", "-danger", f"[dim]cat.prob[/dim] [{sc}]{prob:.4f}[/{sc}] [green]<0.10 OK[/green] [yellow]<0.30 WARN[/yellow] [red]≥0.30 CRIT[/red]", int(prob * 100)), ("mc_env_bar", env, 0.40, 0.70, "-danger", "-warning", f"[dim]env.score[/dim] [green]{env:.4f}[/green] [red]<0.40 DANGER[/red] [yellow]<0.70 CAUTION[/yellow] [green]≥0.70 SAFE[/green]", int(env * 100)), ]: pb = self._w(f"#{bar_id}", ProgressBar) pb.progress = fmt pb.remove_class("-danger", "-warning") if val < lo_thr: pb.add_class(lo_cls) elif val < hi_thr: pb.add_class(hi_cls) self._w(f"#{bar_id.replace('_bar','_label')}").update(label) # champion_probability bar chp_val = champ_p if champ_p is not None else 0.0 cb = self._w("#mc_champ_bar", ProgressBar) cb.progress = int(chp_val * 100) cb.remove_class("-danger", "-warning") if chp_val < 0.30: cb.add_class("-danger") elif chp_val < 0.60: cb.add_class("-warning") self._w("#mc_champ_label").update( f"[dim]champ.prob[/dim] " + (f"[green]{champ_p*100:.1f}%[/green]" if champ_p is not None else "[dim]—[/dim]") + " [green]>60% GOOD[/green] [yellow]>30% MARGINAL[/yellow] [red]<30% RISK[/red]" ) # Live performance tier cur_cap = float(cap.get("capital", 0.0)) if cap else 0.0 if cur_cap > 0: if self._session_start_cap is None: self._session_start_cap = cur_cap if self._cap_peak is None or cur_cap > self._cap_peak: self._cap_peak = cur_cap live_roi = ((cur_cap - self._session_start_cap) / self._session_start_cap if cur_cap > 0 and self._session_start_cap else None) live_dd = ((self._cap_peak - cur_cap) / self._cap_peak if cur_cap > 0 and self._cap_peak and cur_cap < self._cap_peak else None) pnl_blue = _S.get("hz.pnl_blue") or {} def _pct(v): return f"{v*100:+.1f}%" if v is not None else "—" def _lm(k, fmt="{:.3f}"): v = pnl_blue.get(k); return fmt.format(v) if v is not None else "—" roi_c = "green" if live_roi and live_roi > 0 else ("red" if live_roi and live_roi < -0.10 else "yellow") dd_c2 = "red" if live_dd and live_dd > 0.20 else ("yellow" if live_dd and live_dd > 0.08 else "green") self._w("#mc_live").update( f"[dim]ROI[/dim] [{roi_c}]{_pct(live_roi):>8}[/{roi_c}]" f" [dim]champ gate:>+30% crit:<-30%[/dim]\n" f"[dim]DD [/dim] [{dd_c2}]{_pct(live_dd):>8}[/{dd_c2}]" f" [dim]champ gate:<20% crit:>40%[/dim]\n" f"[dim]WR:{_lm('win_rate','{:.1%}')} PF:{_lm('profit_factor','{:.2f}')}" f" Sh:{_lm('sharpe','{:.2f}')} Cal:{_lm('calmar','{:.2f}')}[/dim]\n" f"[dim]cap:${cur_cap:,.0f} start:${self._session_start_cap:,.0f} " f"trades:{eng.get('trades_executed','—')}[/dim]" if cur_cap > 0 and self._session_start_cap else "[dim]awaiting capital data…[/dim]" ) # Right: sparklines + legend self._w("#mc_spark_lbl").update( f"[dim]cat.prob history [{min(self._prob_hist):.3f}–{max(self._prob_hist):.3f}][/dim]") self._w("#mc_spark", Sparkline).data = list(self._prob_hist) mae_list = list(self._mae_deque) self._w("#mc_mae_lbl").update(f"[dim]MAE hist (n={len(mae_list)})[/dim]") self._w("#mc_mae_spark", Sparkline).data = mae_list[-40:] if mae_list else [0.0] warn_str = ("\n[yellow]⚠ " + mc_warns[0] + "[/yellow]") if mc_warns else "" self._w("#mc_legend").update( "[bold]MC THRESHOLDS[/bold]\n" "[green]GREEN[/green] cat < 0.10\n" "[yellow]ORANGE[/yellow] cat < 0.30\n" "[red]RED[/red] cat ≥ 0.30\n" "[dim]DD gate: <20%[/dim]\n" "[dim]DD crit: >40%[/dim]" + warn_str ) # ── TEST FOOTER ─────────────────────────────────────────────────────── if self._test_vis: tr = self._read_json(_TEST_JSON) or {} def _tr_badge(cat): info = tr.get(cat, {}) if not info: return f"[dim]{cat[:12]}:n/a[/dim]" p, f = info.get("passed",0), info.get("failed",0) c = "green" if f == 0 else ("yellow" if f <= 2 else "red") return f"[{c}]{cat[:10]}:{p}/{p+f}[/{c}][dim]@{info.get('ts','?')[:10]}[/dim]" cats = ["data_integrity","finance_fuzz","signal_fill","degradation","actor"] self._w("#test_footer").update( f"[bold dim]TESTS[/bold dim] [dim]last:{tr.get('_run_at','never')}[/dim]\n" f"{' '.join(_tr_badge(c) for c in cats)}\n" f"[dim]update: prod/run_logs/test_results_latest.json[/dim]" ) else: self._w("#test_footer").update("") # ── LOG ─────────────────────────────────────────────────────────────── if self._log_vis: self._w("#p_log").update( f"[bold]LOG[/bold] (l=hide)\n" f"[dim]{now_str}[/dim] scan=#{scan_no} vel={vel_div:+.5f}\n" f"hz_err:{_S.get('hz_err','none')} pf_err:{_S.get('prefect_err','none')}\n" f"[dim]state keys:{len(_S._d)} safety_live:{safety_live}[/dim]" ) def action_force_refresh(self) -> None: self._update() def action_toggle_log(self) -> None: self._log_vis = not self._log_vis self.query_one("#log_row").display = self._log_vis def action_toggle_tests(self) -> None: self._test_vis = not self._test_vis; self._update() def _w(self, selector, widget_type=Static): return self.query_one(selector, widget_type) @staticmethod def _read_json(path): try: return json.loads(path.read_text()) except Exception: return None if __name__ == "__main__": DolphinTUI().run()