#!/usr/bin/env python3 """ DOLPHIN TUI v2 — full layout, mock data, sexy MC-Forewarner footer. Run: python3 dolphin_tui_v2.py q=quit r=refresh l=toggle log """ import time import math from collections import deque from textual.app import App, ComposeResult from textual.widgets import Static, ProgressBar, Sparkline, Digits, Rule from textual.containers import Horizontal, Vertical # ── CSS ─────────────────────────────────────────────────────────────────────── CSS = """ Screen { background: #0d0d0d; color: #d0d0d0; } #header { height: 2; background: #111; border: 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 Footer */ #mc_footer_outer { height: 7; border: solid #336; background: #080818; } #mc_title { height: 1; padding: 0 1; } #mc_body { height: 6; } #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_spark_label { height: 1; } #mc_sparkline { height: 2; } #mc_digits { height: 3; } #mc_status_text { height: 2; } ProgressBar > .bar--bar { color: $success; } ProgressBar > .bar--complete { color: $success; } ProgressBar.-danger > .bar--bar { color: $error; } ProgressBar.-warning > .bar--bar { color: $warning; } Static.panel { border: solid #3a3a3a; padding: 0 1; height: 100%; } #panel_trader { width: 1fr; border: solid #00aa88; } #panel_health { width: 1fr; } #panel_alpha { width: 1fr; } #panel_scan { width: 1fr; } #panel_extf { width: 1fr; } #panel_esof { width: 1fr; } #panel_capital { width: 1fr; } #panel_prefect { width: 1fr; } #panel_obf { width: 1fr; } #panel_log { width: 1fr; border: solid #444; padding: 0 1; } """ # ── Helpers ─────────────────────────────────────────────────────────────────── def prefect_dot(status: str, blink_frame: bool) -> str: s = status.upper() if s == "COMPLETED": return "[green]●[/green]" if s == "RUNNING": return "[cyan]◉[/cyan]" if blink_frame else "[dim]◌[/dim]" if s in ("FAILED", "CRASHED"): return "[red]●[/red]" if s == "LATE": return "[dark_orange]●[/dark_orange]" if s == "PENDING": return "[yellow]●[/yellow]" return "[dim]●[/dim]" MOCK_FLOWS = [ ("paper_trade_flow", "COMPLETED", "2m"), ("nautilus_prefect", "COMPLETED", "8m"), ("obf_prefect_flow", "RUNNING", "0m"), ("exf_fetcher_flow", "COMPLETED", "15m"), ("mc_forewarner_flow", "RUNNING", "3m"), ] MOCK_POSITIONS = [ ("BTCUSDT", "SHORT", 0.01, 83420.5, 83278.2), ("ETHUSDT", "SHORT", 0.10, 1612.3, 1598.7), ] def mock_open_positions(n: int) -> list: phase = (n // 20) % 3 if phase == 0: return [] if phase == 1: p = MOCK_POSITIONS[0] return [(p[0], p[1], p[2], p[3], p[4] - (n % 10) * 2.1)] return [(p[0], p[1], p[2], p[3], p[4] - (n % 10) * 1.5) for p in MOCK_POSITIONS] def mc_mock(n: int) -> dict: """Real schema: DOLPHIN_FEATURES['mc_forewarner_latest'] Thresholds: GREEN<0.10 ORANGE<0.30 RED>=0.30""" t = n * 0.05 prob = max(0.0, min(1.0, 0.12 + 0.10 * math.sin(t))) env = max(0.0, min(1.0, 0.82 - 0.08 * abs(math.sin(t * 1.3)))) status = "GREEN" if prob < 0.10 else ("ORANGE" if prob < 0.30 else "RED") return {"status": status, "catastrophic_prob": prob, "envelope_score": env, "source": "MOCK", "timestamp": time.strftime("%H:%M:%SZ", time.gmtime())} # ── App ─────────────────────────────────────────────────────────────────────── class DolphinTUI(App): CSS = CSS BINDINGS = [("q","quit","Quit"),("r","refresh","Refresh"),("l","toggle_log","Log")] _log_visible = False _tick_n = 0 _prob_history: deque = None def compose(self) -> ComposeResult: yield Static("", id="header") with Horizontal(id="trader_row"): yield Static("", classes="panel", id="panel_trader") with Horizontal(id="top_row"): yield Static("", classes="panel", id="panel_health") yield Static("", classes="panel", id="panel_alpha") yield Static("", classes="panel", id="panel_scan") with Horizontal(id="mid_row"): yield Static("", classes="panel", id="panel_extf") yield Static("", classes="panel", id="panel_esof") yield Static("", classes="panel", id="panel_capital") with Horizontal(id="bot_row"): yield Static("", classes="panel", id="panel_prefect") yield Static("", classes="panel", id="panel_obf") # ── MC-Forewarner footer ────────────────────────────────────────────── with Vertical(id="mc_footer_outer"): yield Static("", id="mc_title") with Horizontal(id="mc_body"): # Left: big probability digits with Vertical(id="mc_left"): yield Digits("0.00", id="mc_digits") yield Static("", id="mc_status_text") # Center: progress bars + sparkline 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_spark_label") yield Sparkline([], id="mc_sparkline") # Right: threshold legend + source with Vertical(id="mc_right"): yield Static("", id="mc_legend") with Horizontal(id="log_row"): yield Static("", classes="panel", id="panel_log") def on_mount(self) -> None: self._prob_history = deque([0.12] * 40, maxlen=40) self.set_interval(1, self._update) self._update() def _update(self) -> None: n = self._tick_n self._tick_n += 1 blink = (n % 2 == 0) t = time.strftime("%Y-%m-%d %H:%M:%S UTC", time.gmtime()) cap = 124532.10 + n * 0.5 pnl = 1240.50 + n * 0.1 rm = 0.82 + (n % 10) * 0.01 vel = -0.031 - (n % 5) * 0.002 scan = 59000 + n age = (n % 5) + 0.1 age_col = "green" if age < 15 else "yellow" mc = mc_mock(n) # ── HEADER ──────────────────────────────────────────────────────────── hz = "[green][HZ ✓][/green]" sc = {"GREEN":"green","ORANGE":"dark_orange","RED":"red"}.get(mc["status"],"dim") self.query_one("#header", Static).update( f"[bold cyan]🐬 DOLPHIN-NAUTILUS[/bold cyan] v2.0 │ {t}" f" │ [green]● GREEN[/green] {hz}" f" │ MC:[{sc}]{mc['status']}[/{sc}]\n" f"[dim] localhost:5701 │ q=quit r=refresh l=log[/dim]" ) # ── TRADER ──────────────────────────────────────────────────────────── positions = mock_open_positions(n) pos_lines = " ".join( f"[cyan]{sym}[/cyan] [yellow]{side}[/yellow] {qty}" f"@[dim]{entry:,.0f}[/dim]→[green]{cur:,.0f}[/green]" f"([green]+${abs((entry-cur)*qty):,.1f}[/green])" for sym, side, qty, entry, cur in positions ) if positions else "[dim]NONE[/dim]" vol_ok = "[green]YES[/green]" if (n % 8) < 6 else "[yellow]NO[/yellow]" self.query_one("#panel_trader", Static).update( f"[bold cyan]NAUTILUS-DOLPHIN TRADER[/bold cyan]" f" posture:[green]APEX[/green] bar:{scan} vol:{vol_ok}" f" trades:[cyan]{12+n//30}[/cyan] cap:[cyan]${cap:,.0f}[/cyan]\n" f" open: {pos_lines}\n" f" vel:[yellow]{vel:.5f}[/yellow] thr:-0.02000 pnl:[green]+${pnl:,.2f}[/green]" ) # ── SYSTEM HEALTH ───────────────────────────────────────────────────── self.query_one("#panel_health", Static).update( f"[bold]SYS HEALTH[/bold]\n" f"rm_meta:[green]{rm:.3f}[/green]\n" f"M1:[green]1.0[/green] M2:[green]1.0[/green] M3:[green]1.0[/green]\n" f"M4:[green]1.0[/green] M5:[green]1.0[/green]\n" f"[green]● GREEN[/green]" ) # ── ALPHA ENGINE ────────────────────────────────────────────────────── filled = int(rm * 16) bar = "█" * filled + "░" * (16 - filled) self.query_one("#panel_alpha", Static).update( f"[bold]ALPHA ENGINE[/bold]\n" f"Posture:[green]APEX ●[/green]\n" f"Rm:[green]{bar}[/green]{rm:.2f}\n" f"ACB:1.55x β=0.80\n" f"C1:[green].9[/green] C2:[green].8[/green] C3:[yellow].7[/yellow]" f" C4:[green]1.[/green] C5:[green].9[/green]" ) # ── SCAN BRIDGE ─────────────────────────────────────────────────────── self.query_one("#panel_scan", Static).update( f"[bold]SCAN / NG7[/bold]\n" f"#{scan} age:[{age_col}]{age:.1f}s[/{age_col}]\n" f"vel_div:[{age_col}]{vel:.4f}[/{age_col}]\n" f"w50:-0.0421 w750:-0.0109\n" f"inst:0.0234" ) # ── ExtF ────────────────────────────────────────────────────────────── self.query_one("#panel_extf", Static).update( f"[bold]ExtF[/bold] [green]9/9 ✓[/green]\n" f"fund:[cyan]-0.012[/cyan] dvol:[cyan]62.4[/cyan]\n" f"fng:[yellow]28[/yellow] taker:0.81\n" f"vix:18.2 ls:0.48\n" f"age:[green]4.2s[/green]" ) # ── EsoF ────────────────────────────────────────────────────────────── self.query_one("#panel_esof", Static).update( f"[bold]EsoF[/bold]\n" f"Moon: Waxing Gibbous\n" f"Merc:[green]Normal[/green]\n" f"Sess:London MC:0.42\n" f"age:[green]3.8s[/green]" ) # ── CAPITAL ─────────────────────────────────────────────────────────── self.query_one("#panel_capital", Static).update( f"[bold]CAPITAL[/bold]\n" f"Cap:[cyan]${cap:,.0f}[/cyan]\n" f"DD:[yellow]-3.21%[/yellow]\n" f"PnL:[green]+${pnl:,.2f}[/green]\n" f"Pos:[green]APEX[/green] T:{12+n//30}" ) # ── PREFECT ─────────────────────────────────────────────────────────── flow_lines = "\n".join( f"{prefect_dot(st, blink)} {name:<22} {dur}" for name, st, dur in MOCK_FLOWS ) self.query_one("#panel_prefect", Static).update( f"[bold]PREFECT[/bold] [green]✓[/green]\n{flow_lines}" ) # ── OBF ─────────────────────────────────────────────────────────────── self.query_one("#panel_obf", Static).update( f"[bold]OBF TOP[/bold]\n" f"BTC [green]+0.18[/green] fp:0.72\n" f"ETH [green]+0.12[/green] fp:0.68\n" f"SOL [green]+0.09[/green] fp:0.61\n" f"BNB [red]-0.05[/red] fp:0.51" ) # ── MC-FOREWARNER FOOTER (sexy) ─────────────────────────────────────── prob = mc["catastrophic_prob"] env = mc["envelope_score"] self._prob_history.append(prob) # Title bar self.query_one("#mc_title", Static).update( f"[bold cyan]⚡ MC-FOREWARNER RISK MANIFOLD[/bold cyan]" f" [{sc}]▶ {mc['status']}[/{sc}]" f" [dim]src:{mc['source']} {mc['timestamp']}[/dim]" ) # Left: Digits widget showing probability as large text self.query_one("#mc_digits", Digits).update(f"{prob:.3f}") status_emoji = {"GREEN": "🟢 SAFE", "ORANGE": "🟡 CAUTION", "RED": "🔴 DANGER"}.get(mc["status"], "⚪") self.query_one("#mc_status_text", Static).update( f"[{sc}]{status_emoji}[/{sc}]\n[dim]cat.prob[/dim]" ) # Center: ProgressBar for catastrophic_prob (danger = high value) prob_pct = int(prob * 100) prob_bar = self.query_one("#mc_prob_bar", ProgressBar) prob_bar.progress = prob_pct # Apply danger CSS class based on threshold prob_bar.remove_class("-danger", "-warning") if prob >= 0.30: prob_bar.add_class("-danger") elif prob >= 0.10: prob_bar.add_class("-warning") self.query_one("#mc_prob_label", Static).update( f"[dim]catastrophic_prob[/dim] " f"[green]▏GREEN<0.10[/green] [yellow]▏ORANGE<0.30[/yellow] [red]▏RED≥0.30[/red]" f" [{sc}]{prob:.4f}[/{sc}]" ) # ProgressBar for envelope_score (safe = high value, so invert display) env_pct = int(env * 100) env_bar = self.query_one("#mc_env_bar", ProgressBar) env_bar.progress = env_pct env_bar.remove_class("-danger", "-warning") if env < 0.40: env_bar.add_class("-danger") elif env < 0.70: env_bar.add_class("-warning") self.query_one("#mc_env_label", Static).update( f"[dim]envelope_score [/dim]" f"[red]▏DANGER<0.40[/red] [yellow]▏CAUTION<0.70[/yellow] [green]▏SAFE≥0.70[/green]" f" [green]{env:.4f}[/green]" ) # Sparkline: rolling 40-sample history of catastrophic_prob self.query_one("#mc_spark_label", Static).update( f"[dim]prob history (40s)[/dim] " f"[dim]min:{min(self._prob_history):.3f} " f"max:{max(self._prob_history):.3f}[/dim]" ) self.query_one("#mc_sparkline", Sparkline).data = list(self._prob_history) # Right: threshold legend self.query_one("#mc_legend", Static).update( f"[bold]THRESHOLDS[/bold]\n" f"[green]GREEN[/green] prob < 0.10\n" f"[yellow]ORANGE[/yellow] prob < 0.30\n" f"[red]RED[/red] prob ≥ 0.30\n" f"\n" f"[dim]runs every 4h[/dim]\n" f"[dim]model: DolphinForewarner[/dim]" ) # ── LOG ─────────────────────────────────────────────────────────────── if self._log_visible: self.query_one("#panel_log", Static).update( f"[bold]LOG[/bold] (l=hide)\n" f"[dim]{t}[/dim] [INFO] RM_META=0.923 GREEN\n" f"[dim]{t}[/dim] [INFO] SCAN #{scan} vel={vel:.4f}\n" f"[dim]{t}[/dim] [INFO] MC {mc['status']} prob={prob:.4f}" ) def action_refresh(self) -> None: self._update() def action_toggle_log(self) -> None: self._log_visible = not self._log_visible self.query_one("#log_row").display = self._log_visible if __name__ == "__main__": DolphinTUI().run()