#!/usr/bin/env python3 """ DOLPHIN TUI v3 — Live, event-driven observability ================================================== All panels driven by real data. Zero additional load on origin systems: • HZ maps → entry listeners (push model; callbacks fire on change only) • Prefect → polled via SDK every 60 s • Capital / meta-health → HZ listener + disk fallback Run: python3 dolphin_tui_v3.py Keys: q=quit r=force-refresh l=toggle log t=toggle test footer HZ maps consumed: DOLPHIN_FEATURES / latest_eigen_scan DOLPHIN_FEATURES / exf_latest DOLPHIN_FEATURES / acb_boost DOLPHIN_FEATURES / obf_universe_latest DOLPHIN_FEATURES / mc_forewarner_latest DOLPHIN_META_HEALTH / latest DOLPHIN_SAFETY / latest DOLPHIN_STATE_BLUE / capital_checkpoint DOLPHIN_HEARTBEAT / nautilus_flow_heartbeat """ # ── stdlib ──────────────────────────────────────────────────────────────────── import asyncio import json import math import threading import 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 # ── paths ───────────────────────────────────────────────────────────────────── _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") _PREFECT_BASE = "http://localhost:4200" # ── symbols watched in OBF panel ────────────────────────────────────────────── _OBF_SYMS = ["BTCUSDT", "ETHUSDT", "SOLUSDT", "BNBUSDT", "XRPUSDT"] # ── color maps ──────────────────────────────────────────────────────────────── _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 store # ═════════════════════════════════════════════════════════════════════════════ class _State: """Lock-guarded dict shared between HZ listener threads and the Textual loop.""" def __init__(self): self._l = threading.Lock() self._d: Dict[str, Any] = {} def put(self, k: str, v: Any): with self._l: self._d[k] = v def get(self, k: str, default=None): with self._l: return self._d.get(k, default) def update(self, mapping: dict): with self._l: self._d.update(mapping) _S = _State() # ═════════════════════════════════════════════════════════════════════════════ # HZ entry-listener adapter (runs in a daemon thread, calls back to Textual) # ═════════════════════════════════════════════════════════════════════════════ def _ingest(key: str, raw: Optional[str]): """Parse JSON payload and store in shared state.""" 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): """ Daemon thread: connect, initial-fetch all keys, attach entry listeners. on_scan: callable invoked (from HZ thread) when latest_eigen_scan arrives. Reconnects automatically on disconnect. """ 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) # ── DOLPHIN_FEATURES ───────────────────────────────────── 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) # ── DOLPHIN_META_HEALTH ────────────────────────────────── mhm = client.get_map("DOLPHIN_META_HEALTH").blocking() _ingest("meta_health", mhm.get("latest")) def _mh(e): if e.key == "latest": _ingest("meta_health", e.value) mhm.add_entry_listener(include_value=True, updated=_mh, added=_mh) # ── DOLPHIN_SAFETY ─────────────────────────────────────── sm = client.get_map("DOLPHIN_SAFETY").blocking() _ingest("safety", sm.get("latest")) def _sf(e): if e.key == "latest": _ingest("safety", e.value) sm.add_entry_listener(include_value=True, updated=_sf, added=_sf) # ── DOLPHIN_STATE_BLUE ─────────────────────────────────── 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) # ── DOLPHIN_PNL_BLUE (live trade performance) ──────────── try: pnl_m = client.get_map("DOLPHIN_PNL_BLUE").blocking() _ingest("pnl_blue", pnl_m.get("session_perf")) def _pnl(e): if e.key == "session_perf": _ingest("pnl_blue", e.value) pnl_m.add_entry_listener(include_value=True, updated=_pnl, added=_pnl) except Exception: pass # map may not exist yet — silent until event trader wires it # ── DOLPHIN_HEARTBEAT ──────────────────────────────────── hbm = client.get_map("DOLPHIN_HEARTBEAT").blocking() _ingest("heartbeat", hbm.get("nautilus_flow_heartbeat")) def _hb(e): if e.key == "nautilus_flow_heartbeat": _ingest("heartbeat", e.value) hbm.add_entry_listener(include_value=True, updated=_hb, added=_hb) # Block until disconnected 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() # ═════════════════════════════════════════════════════════════════════════════ # Prefect poll (asyncio task, 60s interval) # ═════════════════════════════════════════════════════════════════════════════ async def prefect_poll_loop(): """Poll Prefect SDK for latest flow-run status per flow. 60s interval.""" 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) # ═════════════════════════════════════════════════════════════════════════════ # Helper utilities # ═════════════════════════════════════════════════════════════════════════════ def _age(ts: float) -> str: """Render age of a Unix timestamp as compact string.""" 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: float, warn=15, dead=60) -> str: s = time.time() - ts if ts else 9999 if s > dead: return "red" if s > warn: return "yellow" return "green" def _bar(v: float, width=12) -> str: """ASCII fill bar 0-1.""" v = max(0.0, min(1.0, v)) f = round(v * width) return "█" * f + "░" * (width - f) def _fmt_vel(v) -> str: if v is None: return "---" return f"{float(v):+.5f}" def _dot(state: str) -> str: 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: str) -> str: c = _PC.get(p, "dim") return f"[{c}]{p}[/{c}]" def _col(v, c): return f"[{c}]{v}[/{c}]" def _eigen_from_scan(scan: dict): """Extract normalised eigen fields from both NG7 (nested result) and NG8 (flat) HZ formats.""" if not scan: return {} # NG7 wraps in "result" r = scan.get("result", scan) mwr_raw = r.get("multi_window_results", {}) # Keys may be ints or strings 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", {}) # NG8 flat format also stores w50_velocity etc. 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_50 = _rs(50).get("instability_score", 0.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), "inst50": float(inst_50 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; } /* Tier 1: envelope assessment */ #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; } /* Tier 2: live perf (ROI/DD/WR + MAE rows + leverage slider) */ #mc_live { height: 8; } /* Right column */ #mc_spark_lbl { height: 1; } #mc_spark { height: 2; } #mc_mae_spark { height: 2; } #mc_mae_lbl { height: 1; } #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; } """ # ═════════════════════════════════════════════════════════════════════════════ # Textual 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 # ── Layout ─────────────────────────────────────────────────────────────── 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"): # ── Tier 1: envelope assessment (4h) ── 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") # ── Tier 2: live performance vs MC bounds ── 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._session_start_cap: Optional[float] = None # first capital seen → ROI denominator self._cap_peak: Optional[float] = None # peak capital → live DD numerator self._mae_deque: deque = deque(maxlen=500) # per-trade MAE values, newest last # Event-driven callback: fires from HZ listener thread start_hz_listener(on_scan=lambda: self.call_from_thread(self._update)) # Prefect async poll self.run_worker(prefect_poll_loop(), name="prefect-poll", exclusive=True) # Periodic fallback refresh (catches non-HZ sources + any missed events) self.set_interval(1.0, self._update) self._update() # ── Master render ──────────────────────────────────────────────────────── 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) # Pull all live data from state mh = _S.get("hz.meta_health") or self._read_json(_META_JSON) safe = _S.get("hz.safety", {}) scan = _S.get("hz.latest_eigen_scan", {}) exf = _S.get("hz.exf_latest", {}) acb = _S.get("hz.acb_boost", {}) obf_u = _S.get("hz.obf_universe_latest", {}) mc = _S.get("hz.mc_forewarner_latest", {}) cap = _S.get("hz.capital") or self._read_json(_CAPITAL_JSON) or {} hb = _S.get("hz.heartbeat", {}) eigen = _eigen_from_scan(scan) # ── HEADER ─────────────────────────────────────────────────────────── 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") posture= (safe or {}).get("posture", "?") pc_col = _PC.get(posture, "dim") hz_tag = "[green][HZ✓][/green]" if hz_up else "[red][HZ✗][/red]" mc_st = (mc or {}).get("status", "N/A") mc_col = _MC.get(mc_st, "dim") 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}]\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) scan_ts = eigen.get("timestamp", 0) vol_ok_s = "[green]VOL✓[/green]" # placeholder — would need vol calc btc_p = eigen.get("btc_price") btc_str = f"BTC:[cyan]${btc_p:,.0f}[/cyan] " if btc_p else "" regime = eigen.get("regime", "?") 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}" f" regime:[yellow]{regime}[/yellow] {vol_ok_s}\n" f" vel_div:[{vc}]{vel_div:+.5f}[/{vc}] thr:[dim]-0.02000[/dim]" f" cap:[cyan]${cap_val:,.0f}[/cyan]\n" f" open-pos: [dim]read DOLPHIN_PNL_BLUE (not yet wired)[/dim]" ) # ── SYS HEALTH ─────────────────────────────────────────────────────── if mh: svc = mh.get("service_status", {}) hz_ks = mh.get("hz_key_status", {}) def _svc_dot(nm): st = svc.get(nm, "?") return "[green]●[/green]" if st=="RUNNING" else "[red]●[/red]" def _hz_dot(nm): info = hz_ks.get(nm, {}) sc = info.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['m4_control_plane']:.2f}" f" m1d:{mh['m1_data_infra']:.2f}" f" m3:{mh['m3_data_freshness']:.2f}" f" m5:{mh['m5_coherence']:.2f}\n" f"exf:{_svc_dot('dolphin_data:exf_fetcher')}" f" acb:{_svc_dot('dolphin_data:acb_processor')}" f" obf:{_svc_dot('dolphin_data:obf_universe')}\n" f"tdr:{_svc_dot('dolphin:nautilus_trader')}" f" scb:{_svc_dot('dolphin: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 (SurvivalStack) ────────────────────────────────────── rm_s = float((safe or {}).get("Rm", 0.0)) bd = (safe or {}).get("breakdown", {}) 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}]" self._w("#p_alpha").update( f"[bold]ALPHA ENGINE[/bold] {_posture_markup(posture)}\n" f"Rm:[{_PC.get(posture,'dim')}]{_bar(rm_s,14)}[/{_PC.get(posture,'dim')}]{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]" ) # ── SCAN / NG8 ──────────────────────────────────────────────────────── scan_age = _age(eigen.get("timestamp")) if eigen.get("timestamp") else "?" scan_ac = _age_col(eigen.get("timestamp", 0), 15, 60) 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") def _exf_str(): if not exf: return "[bold]ExtF[/bold]\n[dim]no data[/dim]" _f = f"{f_btc:.5f}" if f_btc is not None else "?" _dv = f"{dvol:.1f}" if dvol is not None else "?" _fg = f"{int(fng)}" if fng is not None else "?" _tk = f"{taker:.3f}" if taker is not None else "?" _ls = f"{ls_btc:.3f}"if ls_btc is not None else "?" _vx = f"{vix:.1f}" if vix is not None else "?" return ( f"[bold]ExtF[/bold] [{exf_ac}]{ok_cnt}/9 {exf_age}[/{exf_ac}]\n" f"fund:[cyan]{_f}[/cyan] dvol:[{dvol_c}]{_dv}[/{dvol_c}]\n" f"fng:[{fng_c}]{_fg}[/{fng_c}] taker:[yellow]{_tk}[/yellow]\n" f"ls_btc:{_ls} vix:{_vx}\n" f"acb✓:{exf.get('_acb_ready','?')}" ) self._w("#p_extf").update(_exf_str()) # ── 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}]" f" 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 "?" # Drawdown from safety Cat5 (≈1 at <5%DD, ≈0.5 at 12%) c5 = bd.get("Cat5", 1.0) if bd else 1.0 # Invert sigmoid: DD_approx = 0.12 + ln((1/c5)-1)/30 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 w750_thr = acb.get("w750_threshold", 0.0) if acb else 0.0 acb_date = acb.get("date", "?") if acb else "?" 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_date}[/{acb_ac}]\n" f"boost:[{boost_c}]{boost:.2f}x[/{boost_c}]" f" β={beta:.2f}" f" cut:[{cut_c}]{cut:.2f}[/{cut_c}]\n" f"w750_thr:{w750_thr:.4f}\n" f"[dim]factors: 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 FOOTER ───────────────────────────────────────────── # --- Session capital tracking (for live ROI + DD) --- cur_cap = float((cap or {}).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 eng_snap = _S.get("hz.engine_snapshot", {}) or {} trades_ex = eng_snap.get("trades_executed", None) # Live ROI and session DD (computable from capital alone) live_roi: Optional[float] = None live_dd: Optional[float] = None if cur_cap > 0 and self._session_start_cap and self._session_start_cap > 0: live_roi = (cur_cap - self._session_start_cap) / self._session_start_cap if cur_cap > 0 and self._cap_peak and self._cap_peak > 0 and cur_cap < self._cap_peak: live_dd = (self._cap_peak - cur_cap) / self._cap_peak # MC envelope fields prob = float((mc or {}).get("catastrophic_prob", 0.0)) env = float((mc or {}).get("envelope_score", 0.0)) champ_p = (mc or {}).get("champion_probability", None) pred_roi = (mc or {}).get("predicted_roi", None) pred_roi_lo= (mc or {}).get("predicted_roi_p10", None) pred_roi_hi= (mc or {}).get("predicted_roi_p90", None) pred_dd = (mc or {}).get("predicted_max_dd", None) mc_warns = (mc or {}).get("warnings", []) mc_ts = (mc or {}).get("timestamp", None) sc = _MC.get(mc_st, "dim") self._prob_hist.append(prob) # Staleness: time since last 4h MC run mc_age_str = "—" 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 # Title row self._w("#mc_title").update( f"[bold cyan]⚡ MC-FOREWARNER RISK MANIFOLD[/bold cyan]" f" [{sc}]▶ {mc_st}[/{sc}]" f" [dim]src:{(mc or {}).get('source','N/A')} assessed:{mc_age_str} next:~4h cadence[/dim]" if mc else "[bold cyan]⚡ MC-FOREWARNER RISK MANIFOLD[/bold cyan]" " [yellow]awaiting HZ data (Prefect 4h schedule)[/yellow]" ) # Left: big cat.prob digits + status summary 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" f"[dim]cat.prob {champ_str}[/dim]\n" f"[dim]env:{env:.3f}[/dim]" ) # Center tier 1: catastrophic_prob bar pb = self._w("#mc_prob_bar", ProgressBar) pb.progress = int(prob * 100) pb.remove_class("-danger", "-warning") if prob >= 0.30: pb.add_class("-danger") elif prob >= 0.10: pb.add_class("-warning") self._w("#mc_prob_label").update( f"[dim]cat.prob[/dim] [{sc}]{prob:.4f}[/{sc}]" f" [green]<0.10 OK[/green] [yellow]<0.30 WARN[/yellow] [red]≥0.30 CRIT[/red]" ) # envelope_score bar (inverted: low = danger) eb = self._w("#mc_env_bar", ProgressBar) eb.progress = int(env * 100) eb.remove_class("-danger", "-warning") if env < 0.40: eb.add_class("-danger") elif env < 0.70: eb.add_class("-warning") self._w("#mc_env_label").update( f"[dim]env.score[/dim] [green]{env:.4f}[/green]" f" [red]<0.40 DANGER[/red] [yellow]<0.70 CAUTION[/yellow] [green]≥0.70 SAFE[/green]" ) # 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]" ) # Center tier 2: live performance vs MC predicted bounds def _pct(v): return f"{v*100:+.1f}%" if v is not None else "—" def _edge_bar(live, lo, hi, width=18, invert=False): """ASCII distance-to-edge bar. live/lo/hi all same units (fraction).""" if live is None or lo is None or hi is None: return "[dim]" + "·" * width + "[/dim]" span = hi - lo if span <= 0: return "[dim]?[/dim]" pos = max(0.0, min(1.0, (live - lo) / span)) if invert: # for DD: high = closer to danger pos = 1.0 - pos idx = int(pos * (width - 1)) bar = "·" * idx + "|" + "·" * (width - 1 - idx) col = "green" if pos > 0.40 else ("yellow" if pos > 0.15 else "red") return f"[{col}]{bar}[/{col}]" # ROI line — MC hard thresholds from mc_metrics.py: # strongly_profitable (champion gate) : ROI > +30% # L_catastrophic : ROI < -30% # Bar spans -30% → +30% (champion gate); position = distance from catastrophic edge _ROI_CRIT = -0.30 # catastrophic lower bound _ROI_CHAMP = 0.30 # champion gate (strongly_profitable) roi_bar = _edge_bar(live_roi, _ROI_CRIT, _ROI_CHAMP) if live_roi is not None else _edge_bar(None, None, None) roi_live = _pct(live_roi) roi_mc = (f"MC p10-p90:[{_pct(pred_roi_lo)},{_pct(pred_roi_hi)}]" if pred_roi_lo is not None else "MC:—") roi_line = f"[dim]ROI [/dim]{roi_live:>8} {roi_bar} [dim]{roi_mc} champ:>{_pct(_ROI_CHAMP)}[/dim]" # DD line — MC hard thresholds from mc_metrics.py: # drawdown_ok (champion gate) : DD < 20% ← crossing here kills champion_prob # L_catastrophic : DD > 40% ← crossing here spikes catastrophic_prob # Bar spans 0% → 40% (catastrophic boundary); 20% marker = champion gate _DD_CHAMP = 0.20 # champion region gate _DD_CRIT = 0.40 # catastrophic label threshold dd_bar = _edge_bar(live_dd, 0.0, _DD_CRIT, invert=True) if live_dd is not None else _edge_bar(None, None, None) dd_live = f"{live_dd*100:.1f}%" if live_dd is not None else "—" dd_champ_dist = f"edge:{(_DD_CHAMP - live_dd)*100:.1f}%rm" if live_dd is not None else "" dd_pred_str = f"champ gate:<{_DD_CHAMP*100:.0f}% crit:>{_DD_CRIT*100:.0f}% {dd_champ_dist}" dd_line = f"[dim]DD [/dim]{dd_live:>8} {dd_bar} [dim]{dd_pred_str}[/dim]" # ── WR / PF / Sharpe / Calmar — from DOLPHIN_PNL_BLUE when wired ────── pnl_blue = _S.get("hz.pnl_blue") or {} def _lm(key, fmt="{:.3f}"): v = pnl_blue.get(key) return fmt.format(v) if v is not None else "—" if pnl_blue: # Ingest per-trade MAE list if present (newest-first list) raw_mae_list = pnl_blue.get("trade_mae_history") or [] if raw_mae_list: # Only extend with values not yet in deque (check by length delta) new_count = max(0, len(raw_mae_list) - len(self._mae_deque)) for v in raw_mae_list[-new_count:]: try: self._mae_deque.append(float(v)) except Exception: pass wr_line = ( f"[dim]WR[/dim] {_lm('win_rate','{:.1%}'):>7} " f"[dim]PF:{_lm('profit_factor','{:.2f}')} " f"Sh:{_lm('sharpe','{:.2f}')} " f"Cal:{_lm('calmar','{:.2f}')}[/dim]" ) if pnl_blue else ( f"[dim]WR/PF/Sharpe/Calmar — awaiting DOLPHIN_PNL_BLUE[/dim]" ) # ── MAE rolling windows ────────────────────────────────────────────── # MAE = max adverse excursion per trade (for SHORTs: max price rise above entry) # Windows: last 1 / 5 / 10 / 25 / 75 / 100 / 250 / 500 trades _MAE_WINDOWS = (1, 5, 10, 25, 75, 100, 250, 500) mae_list = list(self._mae_deque) # O(n) copy, done once per render def _mae_window(n): sl = mae_list[-n:] if len(mae_list) >= n else mae_list return (max(sl) if sl else None) if mae_list: parts = [] for w in _MAE_WINDOWS: v = _mae_window(w) if v is None: parts.append(f"[dim]{w}:—[/dim]") else: col = "green" if v < 0.005 else ("yellow" if v < 0.015 else "red") parts.append(f"[{col}]{w}:{v*100:.2f}%[/{col}]") mae_line1 = "[dim]MAE max/window [/dim]" + " ".join(parts[:4]) mae_line2 = " " + " ".join(parts[4:]) else: src = "DOLPHIN_PNL_BLUE" if not pnl_blue else "trade_mae_history" mae_line1 = f"[dim]MAE — awaiting {src}[/dim]" mae_line2 = "" # ── Leverage slider ────────────────────────────────────────────────── # Sources: engine_snapshot (leverage_soft_cap, leverage_abs_cap, current_leverage) # ACB boost (scales effective ceiling) # Zones: 1x–5x GREEN (MC champion ref) 5x–8x YELLOW (soft cap) 8x–9x RED (abs cap) _LEV_CHAMP = 5.0 # MC champion reference leverage (mc_sampler CHAMPION max_leverage) _LEV_SOFT = float(eng_snap.get("leverage_soft_cap", 8.0)) _LEV_ABS = float(eng_snap.get("leverage_abs_cap", 9.0)) cur_lev = float(eng_snap.get("current_leverage", 0.0)) acb_boost_v= float((acb or {}).get("boost", 1.0)) # Effective ceiling = base soft cap × ACB boost, clamped to abs cap eff_ceil = min(_LEV_SOFT * acb_boost_v, _LEV_ABS) # Build the slider bar (width=40 characters, range 0→_LEV_ABS) _W = 40 def _lev_bar(cur, ceil, soft, champ, abs_cap, width=_W): """ Zones (characters proportional to leverage range 0→abs_cap): 0 ──── champ: green fill / green dot champ ─ soft: yellow fill / yellow dot soft ── abs: red fill / red dot Current position: ◈ (bold white) Ceiling: ┤ marker """ scale = width / abs_cap # Build char array chars = [] for i in range(width): lev_at = (i + 0.5) / scale if lev_at <= champ: chars.append(("█", "green")) elif lev_at <= soft: chars.append(("▓", "yellow")) else: chars.append(("░", "red")) # Place ceiling marker ceil_idx = min(width - 1, int(ceil * scale)) c, _ = chars[ceil_idx] chars[ceil_idx] = ("┤", "bold white") # Place current leverage cursor (if > 0) if cur > 0: cur_idx = min(width - 1, int(cur * scale)) chars[cur_idx] = ("◈", "bold cyan") # Render using run-length encoding for performance out = "" prev_col = None seg = "" for ch, col in chars: if col != prev_col: if seg and prev_col: out += f"[{prev_col}]{seg}[/{prev_col}]" seg = ch prev_col = col else: seg += ch if seg and prev_col: out += f"[{prev_col}]{seg}[/{prev_col}]" return out lev_bar_str = _lev_bar(cur_lev, eff_ceil, _LEV_SOFT, _LEV_CHAMP, _LEV_ABS) cur_lev_str = f"[bold cyan]{cur_lev:.2f}x[/bold cyan]" if cur_lev > 0 else "[dim]—[/dim]" ceil_col = "green" if eff_ceil <= _LEV_CHAMP else ("yellow" if eff_ceil <= _LEV_SOFT else "red") lev_line1 = ( f"[dim]LEV[/dim] [{ceil_col}]{eff_ceil:.1f}x[/{ceil_col}][dim]┤[/dim] " f"{lev_bar_str} " f"[dim]|{_LEV_ABS:.0f}x[/dim] cur:{cur_lev_str} acb:×{acb_boost_v:.3f}" ) lev_line2 = ( f"[dim] 1x{'─'*int((_LEV_CHAMP-1)/_LEV_ABS*_W-2)}" f"5x(champ){'─'*int((_LEV_SOFT-_LEV_CHAMP)/_LEV_ABS*_W-9)}" f"8x(soft){'─'*int((_LEV_ABS-_LEV_SOFT)/_LEV_ABS*_W-8)}9x(abs)[/dim]" ) trades_line = ( f"[dim]trades:{trades_ex if trades_ex is not None else '—'} " f"start:${self._session_start_cap:,.0f} " f"cur:${cur_cap:,.0f}[/dim]" ) if cur_cap > 0 else "[dim]awaiting capital[/dim]" self._w("#mc_live").update( f"{roi_line}\n{dd_line}\n{wr_line}\n" f"{mae_line1}\n{mae_line2}\n" f"{lev_line1}\n{lev_line2}\n" f"{trades_line}" ) # Right column: cat.prob sparkline + MAE sparkline + legend self._w("#mc_spark_lbl").update( f"[dim]cat.prob [{min(self._prob_hist):.3f}–{max(self._prob_hist):.3f}][/dim]" ) self._w("#mc_spark", Sparkline).data = list(self._prob_hist) 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" f"[dim]DD gate: <20%[/dim]\n" f"[dim]DD crit: >40%[/dim]" + warn_str ) # ── TEST RESULTS 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) t = p + f c = "green" if f == 0 else ("yellow" if f <= 2 else "red") ts = info.get("ts", "?")[:10] return f"[{c}]{cat[:10]}:{p}/{t}[/{c}][dim]@{ts}[/dim]" cats = ["data_integrity","finance_fuzz","signal_fill","degradation","actor"] badges = " ".join(_tr_badge(c) for c in cats) last_run = tr.get("_run_at", "never") self._w("#test_footer").update( f"[bold dim]TESTS[/bold dim] [dim]last:{last_run}[/dim]\n" f"{badges}\n" f"[dim]update: prod/run_logs/test_results_latest.json[/dim]" ) else: self._w("#test_footer").update("") # ── LOG (hidden by default) ─────────────────────────────────────────── if self._log_vis: hz_err = _S.get("hz_err", "") pf_err = _S.get("prefect_err", "") 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:{hz_err or 'none'} pf_err:{pf_err or 'none'}\n" f"[dim]state keys: {len(_S._d)}[/dim]" ) # ── Actions ────────────────────────────────────────────────────────────── 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() # ── Helpers ────────────────────────────────────────────────────────────── def _w(self, selector: str, widget_type=Static): return self.query_one(selector, widget_type) @staticmethod def _read_json(path: Path) -> Optional[dict]: try: return json.loads(path.read_text()) except Exception: return None # ═════════════════════════════════════════════════════════════════════════════ # Test-result writer (called by CI / manual test scripts) # ═════════════════════════════════════════════════════════════════════════════ def write_test_results(results: dict): """ Called by test scripts to update the TUI test footer. results = { "data_integrity": {"passed": 15, "failed": 0, "ts": "2026-04-05T10:00"}, "degradation": {"passed": 12, "failed": 0, "ts": "2026-04-05T10:01"}, ... "_run_at": "2026-04-05T10:01:42Z" } """ _TEST_JSON.parent.mkdir(parents=True, exist_ok=True) _TEST_JSON.write_text(json.dumps(results, indent=2)) # ═════════════════════════════════════════════════════════════════════════════ # Entry point # ═════════════════════════════════════════════════════════════════════════════ if __name__ == "__main__": DolphinTUI().run()