664 lines
24 KiB
Python
664 lines
24 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""DOLPHIN GREEN live status — v1
|
||
|
|
WHITE-on-DARK theme. Reads GREEN-specific HZ maps + shared infrastructure maps.
|
||
|
|
Parses /tmp/green_launch.log for live engine state (LATENCY scan lines).
|
||
|
|
|
||
|
|
Data source mapping:
|
||
|
|
GREEN-unique : DOLPHIN_STATE_GREEN, DOLPHIN_PNL_GREEN, /tmp/green_launch.log
|
||
|
|
Shared : DOLPHIN_SAFETY, DOLPHIN_META_HEALTH, DOLPHIN_FEATURES, DOLPHIN_HEARTBEAT
|
||
|
|
|
||
|
|
Run: source /home/dolphin/siloqy_env/bin/activate && python dolphin_status_green.py
|
||
|
|
Quit: Ctrl-C
|
||
|
|
"""
|
||
|
|
|
||
|
|
import json, math, os, re, threading, time, sys, urllib.request, urllib.parse
|
||
|
|
from collections import deque
|
||
|
|
from datetime import datetime, timezone
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
import hazelcast
|
||
|
|
|
||
|
|
# ── GREEN configuration ─────────────────────────────────────────────────────────
|
||
|
|
STRATEGY = "green"
|
||
|
|
STATE_MAP = "DOLPHIN_STATE_GREEN"
|
||
|
|
PNL_MAP = "DOLPHIN_PNL_GREEN"
|
||
|
|
SAFETY_MAP = "DOLPHIN_SAFETY" # shared
|
||
|
|
META_MAP = "DOLPHIN_META_HEALTH" # shared
|
||
|
|
FEAT_MAP = "DOLPHIN_FEATURES" # shared
|
||
|
|
HB_MAP = "DOLPHIN_HEARTBEAT" # shared (BLUE heartbeat — reference only)
|
||
|
|
GREEN_LOG = Path("/tmp/green_launch.log")
|
||
|
|
|
||
|
|
# ── ClickHouse fire-and-forget write ─────────────────────────────────────────
|
||
|
|
_CH_URL = "http://localhost:8123"
|
||
|
|
_CH_USER = "dolphin"
|
||
|
|
_CH_PASS = "dolphin_ch_2026"
|
||
|
|
_CH_Q: deque = deque(maxlen=500)
|
||
|
|
|
||
|
|
def _ch_worker():
|
||
|
|
while True:
|
||
|
|
time.sleep(2)
|
||
|
|
rows = []
|
||
|
|
while _CH_Q:
|
||
|
|
try: rows.append(_CH_Q.popleft())
|
||
|
|
except IndexError: break
|
||
|
|
if not rows: continue
|
||
|
|
body = "\n".join(json.dumps(r) for r in rows).encode()
|
||
|
|
url = f"{_CH_URL}/?database=dolphin&query=INSERT+INTO+status_snapshots+FORMAT+JSONEachRow"
|
||
|
|
req = urllib.request.Request(url, data=body, method="POST")
|
||
|
|
req.add_header("X-ClickHouse-User", _CH_USER)
|
||
|
|
req.add_header("X-ClickHouse-Key", _CH_PASS)
|
||
|
|
req.add_header("Content-Type", "application/octet-stream")
|
||
|
|
try: urllib.request.urlopen(req, timeout=4)
|
||
|
|
except Exception: pass
|
||
|
|
|
||
|
|
threading.Thread(target=_ch_worker, daemon=True, name="ch-status-green").start()
|
||
|
|
|
||
|
|
def ch_put(row: dict):
|
||
|
|
_CH_Q.append(row)
|
||
|
|
|
||
|
|
# ── ANSI theme ──────────────────────────────────────────────────────────────────
|
||
|
|
CLEAR = "\033[2J\033[H"
|
||
|
|
BOLD = "\033[1m"; DIM = "\033[2m"; RST = "\033[0m"
|
||
|
|
|
||
|
|
# GREEN TUI theme — bright white primary on dark green header
|
||
|
|
BW = "\033[97m" # bright white (replaces CYAN from BLUE TUI)
|
||
|
|
DG = "\033[38;5;46m" # bright green accent
|
||
|
|
BG_HDR = "\033[48;5;22m" # dark green background for header banner
|
||
|
|
LG = "\033[38;5;82m" # light green
|
||
|
|
GREEN = "\033[32m"; YELLOW = "\033[33m"; RED = "\033[31m"; CYAN = "\033[36m"
|
||
|
|
ORANGE = "\033[38;5;208m"
|
||
|
|
|
||
|
|
PC = {"APEX": GREEN, "STALKER": YELLOW, "TURTLE": ORANGE, "HIBERNATE": RED}
|
||
|
|
SC = {"GREEN": GREEN, "DEGRADED": YELLOW, "CRITICAL": ORANGE, "DEAD": RED}
|
||
|
|
|
||
|
|
# Thresholds (same algorithm — shared with BLUE)
|
||
|
|
VEL_DIV_THRESHOLD = -0.020
|
||
|
|
VEL_DIV_EXTREME = -0.050
|
||
|
|
VEL_DIV_WARN = -0.010
|
||
|
|
VEL_DIV_CLOSE = -0.015
|
||
|
|
VOL_P60 = 0.00026414
|
||
|
|
BTC_VOL_WINDOW = 50
|
||
|
|
FIXED_TP_PCT = 0.0095
|
||
|
|
MAX_HOLD_BARS = 250
|
||
|
|
OB_IMBALANCE_BIAS = -0.09
|
||
|
|
|
||
|
|
START_CAP = None
|
||
|
|
CAP_PEAK = None
|
||
|
|
|
||
|
|
# ── Log parsing ─────────────────────────────────────────────────────────────────
|
||
|
|
_RE_ANSI = re.compile(r'\x1b\[[0-9;]*m')
|
||
|
|
_RE_LATENCY = re.compile(
|
||
|
|
r"LATENCY scan #(\d+): bar=(\d+) vel_div=([-\d.]+) step_bar=([\d.]+)ms vol_ok=(\w+)"
|
||
|
|
)
|
||
|
|
_RE_TS = re.compile(r"^(\S+Z)\s+")
|
||
|
|
_RE_ENTRY = re.compile(r"ENTRY: (\{.+?\})(?:\s*\[.*\])?$")
|
||
|
|
_RE_EXIT = re.compile(r"EXIT: (\{.+?\})(?:\s*\[.*\])?$")
|
||
|
|
|
||
|
|
def _strip_ansi(s: str) -> str:
|
||
|
|
return _RE_ANSI.sub('', s)
|
||
|
|
|
||
|
|
|
||
|
|
def _parse_log_dict(raw: str) -> dict:
|
||
|
|
import ast
|
||
|
|
cleaned = raw.replace(": nan", ": null").replace(": inf", ": null").replace(": -inf", ": null")
|
||
|
|
try:
|
||
|
|
return ast.literal_eval(raw)
|
||
|
|
except Exception:
|
||
|
|
pass
|
||
|
|
try:
|
||
|
|
return json.loads(cleaned.replace("'", '"'))
|
||
|
|
except Exception:
|
||
|
|
raise ValueError(f"unparseable: {raw[:80]}")
|
||
|
|
|
||
|
|
|
||
|
|
def _parse_green_state(n_lines=600):
|
||
|
|
"""Parse last N lines of GREEN log for live engine state + trade history."""
|
||
|
|
try:
|
||
|
|
raw_lines = GREEN_LOG.read_text(errors="replace").splitlines()[-n_lines:]
|
||
|
|
except Exception:
|
||
|
|
return {}, [], []
|
||
|
|
|
||
|
|
lines = [_strip_ansi(l) for l in raw_lines]
|
||
|
|
|
||
|
|
state = {}
|
||
|
|
for line in lines:
|
||
|
|
m = _RE_LATENCY.search(line)
|
||
|
|
if m:
|
||
|
|
ts_m = _RE_TS.match(line)
|
||
|
|
state = {
|
||
|
|
"last_scan_number": int(m.group(1)),
|
||
|
|
"bar_idx": int(m.group(2)),
|
||
|
|
"last_vel_div": float(m.group(3)),
|
||
|
|
"step_bar_ms": float(m.group(4)),
|
||
|
|
"vol_ok": m.group(5) == "True",
|
||
|
|
"last_ts": ts_m.group(1) if ts_m else "",
|
||
|
|
}
|
||
|
|
|
||
|
|
entries = {}
|
||
|
|
trades = []
|
||
|
|
for line in lines:
|
||
|
|
m = _RE_ENTRY.search(line)
|
||
|
|
if m:
|
||
|
|
ts_m = _RE_TS.match(line)
|
||
|
|
try:
|
||
|
|
d = _parse_log_dict(m.group(1))
|
||
|
|
entries[d["trade_id"]] = {"ts": ts_m.group(1) if ts_m else "", **d}
|
||
|
|
except Exception:
|
||
|
|
pass
|
||
|
|
m = _RE_EXIT.search(line)
|
||
|
|
if m:
|
||
|
|
ts_m = _RE_TS.match(line)
|
||
|
|
try:
|
||
|
|
d = _parse_log_dict(m.group(1))
|
||
|
|
tid = d.get("trade_id")
|
||
|
|
if tid and tid in entries:
|
||
|
|
e = entries.pop(tid)
|
||
|
|
trades.append({**e, "exit_ts": ts_m.group(1) if ts_m else "",
|
||
|
|
"reason": d.get("reason", "?"),
|
||
|
|
"pnl_pct": d.get("pnl_pct", 0),
|
||
|
|
"net_pnl": d.get("net_pnl", 0),
|
||
|
|
"bars_held": d.get("bars_held", 0)})
|
||
|
|
except Exception:
|
||
|
|
pass
|
||
|
|
|
||
|
|
errors = [l for l in lines if re.search(r'\[ERROR\]|\[WARNING\]|\[STALE', l)][-5:]
|
||
|
|
|
||
|
|
return state, trades[-5:], errors[-3:]
|
||
|
|
|
||
|
|
|
||
|
|
# ── Helpers ─────────────────────────────────────────────────────────────────────
|
||
|
|
def _age(ts):
|
||
|
|
if not ts: return "?"
|
||
|
|
if isinstance(ts, str):
|
||
|
|
try:
|
||
|
|
dt = datetime.fromisoformat(ts.replace("Z", "+00:00"))
|
||
|
|
s = time.time() - dt.timestamp()
|
||
|
|
except Exception:
|
||
|
|
return "?"
|
||
|
|
else:
|
||
|
|
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_seconds(ts_str):
|
||
|
|
try:
|
||
|
|
dt = datetime.fromisoformat(ts_str.replace("Z", "+00:00"))
|
||
|
|
return time.time() - dt.timestamp()
|
||
|
|
except Exception:
|
||
|
|
return 9999
|
||
|
|
|
||
|
|
|
||
|
|
def _bar(v, w=20):
|
||
|
|
v = max(0.0, min(1.0, v))
|
||
|
|
return "\u2588" * round(v * w) + "\u2591" * (w - round(v * w))
|
||
|
|
|
||
|
|
|
||
|
|
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 _item(label, color, val=""):
|
||
|
|
dot = f"{color}\u25cf{RST}"
|
||
|
|
v = f":{val}" if val else ""
|
||
|
|
return f"{dot}{DIM}{label}{v}{RST}"
|
||
|
|
|
||
|
|
|
||
|
|
def _vel_item(vel_div):
|
||
|
|
v = f"{vel_div:+.4f}"
|
||
|
|
if vel_div <= VEL_DIV_EXTREME:
|
||
|
|
return _item("vel_div", GREEN, v)
|
||
|
|
elif vel_div <= VEL_DIV_THRESHOLD:
|
||
|
|
return _item("vel_div", GREEN, v)
|
||
|
|
elif vel_div <= VEL_DIV_CLOSE:
|
||
|
|
return _item("vel_div", YELLOW, v)
|
||
|
|
elif vel_div <= VEL_DIV_WARN:
|
||
|
|
return _item("vel_div", ORANGE, v)
|
||
|
|
elif vel_div < 0:
|
||
|
|
return _item("vel_div", RED, v)
|
||
|
|
else:
|
||
|
|
return _item("vel_div", RED, v)
|
||
|
|
|
||
|
|
|
||
|
|
def signal_fired(vel_div, vol_ok, posture, acb_ready, exf_ok, halt):
|
||
|
|
return (
|
||
|
|
vel_div <= VEL_DIV_THRESHOLD
|
||
|
|
and vol_ok
|
||
|
|
and posture not in ("HIBERNATE", "TURTLE")
|
||
|
|
and acb_ready
|
||
|
|
and exf_ok
|
||
|
|
and not halt
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def trade_can_execute(open_count, lev, abs_cap, daily_loss_ok, boost):
|
||
|
|
return (
|
||
|
|
open_count == 0
|
||
|
|
and lev < abs_cap
|
||
|
|
and daily_loss_ok
|
||
|
|
and boost > 0
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def _best_fill_candidate(obf_universe):
|
||
|
|
candidates = []
|
||
|
|
for k, v in obf_universe.items():
|
||
|
|
if not isinstance(v, dict) or "fill_probability" not in v:
|
||
|
|
continue
|
||
|
|
candidates.append((k, v))
|
||
|
|
if not candidates:
|
||
|
|
return None, {}
|
||
|
|
def score(item):
|
||
|
|
sym, a = item
|
||
|
|
imb = float(a.get("imbalance", 0))
|
||
|
|
fp = float(a.get("fill_probability", 0))
|
||
|
|
sp = float(a.get("spread_bps", 99))
|
||
|
|
dq = float(a.get("depth_quality", 0))
|
||
|
|
imb_bonus = max(0.0, -imb)
|
||
|
|
return fp * (1 + imb_bonus) * dq / max(0.1, sp)
|
||
|
|
candidates.sort(key=score, reverse=True)
|
||
|
|
return candidates[0]
|
||
|
|
|
||
|
|
|
||
|
|
# ── Gear rows ──────────────────────────────────────────────────────────────────
|
||
|
|
def gear_rows(gstate, safe, acb, exf, hb, obf_universe=None):
|
||
|
|
vel_div = float(gstate.get("last_vel_div", 0) or 0)
|
||
|
|
vol_ok = bool(gstate.get("vol_ok", False))
|
||
|
|
posture = safe.get("posture") or gstate.get("posture") or "?"
|
||
|
|
halt = posture in ("HIBERNATE", "TURTLE")
|
||
|
|
|
||
|
|
acb_boost_val = float(acb.get("boost", acb.get("cut", 0)) or 0)
|
||
|
|
acb_ready = acb_boost_val > 0
|
||
|
|
exf_ok_count = int(exf.get("_ok_count", 0) if exf else 0)
|
||
|
|
exf_ok = exf_ok_count >= 3
|
||
|
|
|
||
|
|
hb_ts = hb.get("ts")
|
||
|
|
hb_ok = bool(hb_ts and (time.time() - hb_ts) < 30)
|
||
|
|
|
||
|
|
# ── SIGNAL ROW ────────────────────────────────────────────────────────────
|
||
|
|
s_items = []
|
||
|
|
|
||
|
|
btc_vol_str = "\u2014"
|
||
|
|
if exf:
|
||
|
|
dvol_raw = exf.get("dvol_btc") or exf.get("dvol")
|
||
|
|
fng_raw = exf.get("fng")
|
||
|
|
if dvol_raw:
|
||
|
|
btc_vol_str = f"dV:{float(dvol_raw):.0f}"
|
||
|
|
if fng_raw:
|
||
|
|
btc_vol_str += f" FnG:{float(fng_raw):.0f}"
|
||
|
|
|
||
|
|
vol_label = f"vol_ok({btc_vol_str})"
|
||
|
|
s_items.append(_item(vol_label,
|
||
|
|
GREEN if vol_ok else RED,
|
||
|
|
"\u2713" if vol_ok else "\u2717 BLOCKED"))
|
||
|
|
|
||
|
|
s_items.append(_vel_item(vel_div))
|
||
|
|
|
||
|
|
pc = PC.get(posture, DIM)
|
||
|
|
s_items.append(_item("posture",
|
||
|
|
GREEN if posture == "APEX" else (YELLOW if posture == "STALKER" else RED),
|
||
|
|
posture))
|
||
|
|
|
||
|
|
s_items.append(_item("acb",
|
||
|
|
GREEN if acb_ready else (ORANGE if acb_boost_val > 0 else RED),
|
||
|
|
f"{acb_boost_val:.2f}"))
|
||
|
|
|
||
|
|
s_items.append(_item("exf",
|
||
|
|
GREEN if exf_ok else (YELLOW if exf_ok_count >= 1 else RED),
|
||
|
|
f"{exf_ok_count}/5"))
|
||
|
|
|
||
|
|
s_items.append(_item("no_halt",
|
||
|
|
GREEN if not halt else RED,
|
||
|
|
"\u2713" if not halt else "HALT"))
|
||
|
|
|
||
|
|
s_items.append(_item("hb",
|
||
|
|
GREEN if hb_ok else RED,
|
||
|
|
_age(hb_ts)))
|
||
|
|
|
||
|
|
all_sig = signal_fired(vel_div, vol_ok, posture, acb_ready, exf_ok, halt)
|
||
|
|
if all_sig:
|
||
|
|
s_items.append(f" {DG}{BOLD}\u25c9 SIGNAL{RST}")
|
||
|
|
|
||
|
|
# ── TRADE ROW ─────────────────────────────────────────────────────────────
|
||
|
|
t_items = []
|
||
|
|
|
||
|
|
t_items.append(_item("paper", DG, "Nautilus"))
|
||
|
|
|
||
|
|
t_items.append(_item("regime",
|
||
|
|
GREEN if not halt else RED,
|
||
|
|
"free" if not halt else "HALTED"))
|
||
|
|
|
||
|
|
rm = float(safe.get("Rm", 0) or 0)
|
||
|
|
t_items.append(_item("Rm",
|
||
|
|
GREEN if rm >= 0.90 else (YELLOW if rm >= 0.70 else (ORANGE if rm >= 0.50 else RED)),
|
||
|
|
f"{rm:.3f}"))
|
||
|
|
|
||
|
|
c5 = float((safe.get("breakdown") or {}).get("Cat5", 1.0) or 1.0)
|
||
|
|
t_items.append(_item("Cat5",
|
||
|
|
GREEN if c5 >= 0.95 else (YELLOW if c5 >= 0.85 else (ORANGE if c5 >= 0.70 else RED)),
|
||
|
|
f"{c5:.3f}"))
|
||
|
|
|
||
|
|
bar_idx = int(gstate.get("bar_idx", 0) or 0)
|
||
|
|
t_items.append(_item("bar", DIM, str(bar_idx)))
|
||
|
|
|
||
|
|
all_trade = all_sig and trade_can_execute(0, 0.0, 8.0, c5 > 0.50, acb_boost_val)
|
||
|
|
if all_trade:
|
||
|
|
t_items.append(f" {DG}{BOLD}\u25c9 TRADE{RST}")
|
||
|
|
|
||
|
|
sig_row = " ".join(s_items)
|
||
|
|
trade_row = " ".join(t_items)
|
||
|
|
|
||
|
|
# ── FILL ROW ──────────────────────────────────────────────────────────────
|
||
|
|
f_items = []
|
||
|
|
|
||
|
|
n_assets = int(obf_universe.get("_n_assets", 0) if obf_universe else 0)
|
||
|
|
n_stale = int(obf_universe.get("_n_stale", 0) if obf_universe else 0)
|
||
|
|
n_fresh = n_assets - n_stale
|
||
|
|
|
||
|
|
f_items.append(_item("universe",
|
||
|
|
GREEN if n_fresh >= 200 else (YELLOW if n_fresh >= 50 else RED),
|
||
|
|
f"{n_fresh}/{n_assets}"))
|
||
|
|
|
||
|
|
sym, ab = _best_fill_candidate(obf_universe or {})
|
||
|
|
if sym:
|
||
|
|
fill_p = float(ab.get("fill_probability", 0))
|
||
|
|
spread = float(ab.get("spread_bps", 99))
|
||
|
|
dq = float(ab.get("depth_quality", 0))
|
||
|
|
imb = float(ab.get("imbalance", 0))
|
||
|
|
depth = float(ab.get("depth_1pct_usd", 0))
|
||
|
|
asset_color = GREEN if fill_p >= 0.80 else (YELLOW if fill_p >= 0.50 else RED)
|
||
|
|
f_items.append(_item("best", asset_color, sym[:6]))
|
||
|
|
f_items.append(_item("fill_p",
|
||
|
|
GREEN if fill_p >= 0.85 else (YELLOW if fill_p >= 0.60 else RED),
|
||
|
|
f"{fill_p:.2f}"))
|
||
|
|
f_items.append(_item("spread",
|
||
|
|
GREEN if spread <= 3 else (YELLOW if spread <= 8 else RED),
|
||
|
|
f"{spread:.1f}bps"))
|
||
|
|
f_items.append(_item("depth_q",
|
||
|
|
GREEN if dq >= 0.5 else (YELLOW if dq >= 0.1 else RED),
|
||
|
|
f"{dq:.2f}"))
|
||
|
|
imb_ok = imb < OB_IMBALANCE_BIAS
|
||
|
|
f_items.append(_item("imb",
|
||
|
|
GREEN if imb_ok else
|
||
|
|
YELLOW if imb < 0 else
|
||
|
|
ORANGE if imb < 0.1 else RED,
|
||
|
|
f"{imb:+.2f}"))
|
||
|
|
f_items.append(_item("depth",
|
||
|
|
GREEN if depth >= 50_000 else (YELLOW if depth >= 10_000 else RED),
|
||
|
|
f"${depth/1000:.0f}k"))
|
||
|
|
else:
|
||
|
|
f_items.append(_item("OBF", RED, "no data"))
|
||
|
|
|
||
|
|
boost = float(acb.get("boost", 1.0) if acb else 1.0)
|
||
|
|
beta = float(acb.get("beta", 0.8) if acb else 0.8)
|
||
|
|
f_items.append(_item("acb_boost",
|
||
|
|
GREEN if boost >= 1.5 else (YELLOW if boost >= 1.0 else ORANGE),
|
||
|
|
f"\u00d7{boost:.2f}"))
|
||
|
|
f_items.append(_item("beta",
|
||
|
|
GREEN if beta >= 0.7 else (YELLOW if beta >= 0.4 else RED),
|
||
|
|
f"{beta:.2f}"))
|
||
|
|
|
||
|
|
f_items.append(f" {DIM}(paper: Nautilus internal){RST}")
|
||
|
|
|
||
|
|
fill = " ".join(f_items)
|
||
|
|
return sig_row, trade_row, fill
|
||
|
|
|
||
|
|
|
||
|
|
# ── Render ──────────────────────────────────────────────────────────────────────
|
||
|
|
def render(hz):
|
||
|
|
global START_CAP, CAP_PEAK
|
||
|
|
|
||
|
|
eng = _get(hz, STATE_MAP, "engine_snapshot")
|
||
|
|
cap = _get(hz, STATE_MAP, "capital_checkpoint")
|
||
|
|
safe = _get(hz, SAFETY_MAP, "latest")
|
||
|
|
hb = _get(hz, HB_MAP, "nautilus_flow_heartbeat")
|
||
|
|
mh = _get(hz, META_MAP, "latest")
|
||
|
|
acb = _get(hz, FEAT_MAP, "acb_boost")
|
||
|
|
exf = _get(hz, FEAT_MAP, "exf_latest")
|
||
|
|
obf = _get(hz, FEAT_MAP, "obf_universe_latest")
|
||
|
|
|
||
|
|
# Log parsing for liveness + trade history (engine_snapshot is primary for state)
|
||
|
|
glog_state, trades_hist, log_errors = _parse_green_state(600)
|
||
|
|
|
||
|
|
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) or 0)
|
||
|
|
hb_ts = hb.get("ts")
|
||
|
|
phase = hb.get("phase", "?")
|
||
|
|
trader_up = hb_ts and (time.time() - hb_ts) < 30
|
||
|
|
scans = eng.get("scans_processed", "\u2014")
|
||
|
|
bar_idx = eng.get("bar_idx", "\u2014")
|
||
|
|
vel_div = float(eng.get("last_vel_div", 0) or 0)
|
||
|
|
vol_ok = bool(eng.get("vol_ok", False))
|
||
|
|
trades_ex = int(eng.get("trades_executed", 0) or 0)
|
||
|
|
lev = float(eng.get("current_leverage", 0) or 0)
|
||
|
|
notional= float(eng.get("open_notional", 0) or 0)
|
||
|
|
mhs_st = mh.get("status", "?")
|
||
|
|
rm_meta = float(mh.get("rm_meta", 0) or 0)
|
||
|
|
snap_ts = eng.get("timestamp", "")
|
||
|
|
|
||
|
|
# Liveness from log (engine_snapshot may lag a few seconds)
|
||
|
|
log_ts_str = glog_state.get("last_ts", "")
|
||
|
|
log_age = _age(log_ts_str) if log_ts_str else "?"
|
||
|
|
green_alive = log_ts_str and _age_seconds(log_ts_str) < 30
|
||
|
|
step_ms = float(glog_state.get("step_bar_ms", 0) or 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 = DG if green_alive else RED
|
||
|
|
roi_c = GREEN if roi >= 0 else RED
|
||
|
|
dd_c = RED if dd > 15 else (YELLOW if dd > 5 else GREEN)
|
||
|
|
|
||
|
|
sig_row, trade_row, fill_row_str = gear_rows(eng, safe, acb, exf, hb, obf)
|
||
|
|
|
||
|
|
svc = mh.get("service_status", {})
|
||
|
|
hz_ks = mh.get("hz_key_status", {})
|
||
|
|
|
||
|
|
L = []
|
||
|
|
|
||
|
|
# HEADER — bright white on dark green background
|
||
|
|
L.append(
|
||
|
|
f"{BG_HDR}{BW}{BOLD} \U0001f42c DOLPHIN-GREEN {RST}"
|
||
|
|
f" {BW}{BOLD}v1{RST} {DIM}{now}{RST} {DG}\u25cf STRATEGY=green{RST}"
|
||
|
|
)
|
||
|
|
L.append("\u2501" * 70)
|
||
|
|
|
||
|
|
# GREEN PROCESS
|
||
|
|
L.append(
|
||
|
|
f"{BW}{BOLD}GREEN{RST} {tc}{'\u25cf LIVE' if green_alive else '\u25cf DOWN'}{RST}"
|
||
|
|
f" log_age:{log_age}"
|
||
|
|
f" scan:#{scans}"
|
||
|
|
f" step:{step_ms:.1f}ms"
|
||
|
|
)
|
||
|
|
L.append(f" {DIM}[shared] BLUE hb:{_age(hb_ts)} phase:{phase}{RST}")
|
||
|
|
|
||
|
|
# SIGNAL → FILL GEARS
|
||
|
|
if not vol_ok:
|
||
|
|
L.append(
|
||
|
|
f" {RED}{BOLD}\u26d4 VOL_OK=FALSE \u2014 engine gate closed,"
|
||
|
|
f" NO trades until BTC vol > {VOL_P60:.6f}{RST}"
|
||
|
|
)
|
||
|
|
L.append(f" {DIM}SIG \u2502{RST} {sig_row}")
|
||
|
|
L.append(f" {DIM}TRD \u2502{RST} {trade_row}")
|
||
|
|
L.append(f" {DIM}FIL \u2502{RST} {fill_row_str}")
|
||
|
|
|
||
|
|
L.append("")
|
||
|
|
|
||
|
|
# CAPITAL
|
||
|
|
L.append(
|
||
|
|
f"{BW}{BOLD}CAPITAL{RST} {BW}${capital:,.2f}{RST}"
|
||
|
|
+ (f" ROI:{roi_c}{roi:+.2f}%{RST} DD:{dd_c}{dd:.2f}%{RST}"
|
||
|
|
f" start:${START_CAP:,.0f}" if START_CAP else "")
|
||
|
|
)
|
||
|
|
L.append(
|
||
|
|
f" trades:{trades_ex} scans:{scans} bar:{bar_idx}"
|
||
|
|
f" lev:{lev:.2f}x notional:${notional:,.0f}"
|
||
|
|
)
|
||
|
|
|
||
|
|
# Open positions (from engine_snapshot — same structure as BLUE)
|
||
|
|
positions = eng.get("open_positions") or []
|
||
|
|
if positions:
|
||
|
|
L.append(f" {BW}{BOLD}OPEN:{RST}")
|
||
|
|
for p in positions:
|
||
|
|
sc2 = GREEN if p.get("side") == "LONG" else RED
|
||
|
|
L.append(f" {sc2}{p.get('asset','?')} {p.get('side','?')}{RST}"
|
||
|
|
f" qty:{p.get('quantity',0):.4f}"
|
||
|
|
f" entry:{p.get('entry_price',0):.2f}"
|
||
|
|
f" upnl:{p.get('unrealized_pnl',0):+.2f}")
|
||
|
|
else:
|
||
|
|
L.append(f" {DIM}no open positions{RST}")
|
||
|
|
|
||
|
|
L.append("")
|
||
|
|
|
||
|
|
# POSTURE (shared map)
|
||
|
|
bd = safe.get("breakdown") or {}
|
||
|
|
L.append(
|
||
|
|
f"{BW}{BOLD}POSTURE{RST} {pc}{posture}{RST}"
|
||
|
|
f" Rm:{pc}{_bar(rm,20)}{RST} {rm:.4f} {DIM}[shared]{RST}"
|
||
|
|
)
|
||
|
|
cats = " ".join(f"C{i}:{float(bd.get(f'Cat{i}',0)):.2f}" for i in range(1,6))
|
||
|
|
L.append(
|
||
|
|
f" {cats} f_env:{float(bd.get('f_env',0)):.3f}"
|
||
|
|
f" f_exe:{float(bd.get('f_exe',0)):.3f}"
|
||
|
|
)
|
||
|
|
|
||
|
|
L.append("")
|
||
|
|
|
||
|
|
# SYS HEALTH (shared map)
|
||
|
|
L.append(
|
||
|
|
f"{BW}{BOLD}SYS HEALTH{RST} {sc}{mhs_st}{RST}"
|
||
|
|
f" rm_meta:{rm_meta:.3f} {DIM}[shared]{RST}"
|
||
|
|
)
|
||
|
|
for m in ("m1_data_infra","m1_trader","m2_heartbeat",
|
||
|
|
"m3_data_freshness","m4_control_plane","m5_coherence"):
|
||
|
|
v = float(mh.get(m, 0) or 0)
|
||
|
|
c = GREEN if v >= 0.9 else (YELLOW if v >= 0.5 else RED)
|
||
|
|
L.append(f" {c}{m}:{v:.3f}{RST}")
|
||
|
|
|
||
|
|
L.append(f" {DIM}services:{RST} "
|
||
|
|
+ " ".join(
|
||
|
|
f"{'\u25cf' if st=='RUNNING' else f'{RED}\u25cf{RST}'}{DIM}{n.split(':')[-1]}{RST}"
|
||
|
|
if st == "RUNNING" else
|
||
|
|
f"{RED}\u25cf{DIM}{n.split(':')[-1]}{RST}"
|
||
|
|
for n, st in sorted(svc.items())))
|
||
|
|
|
||
|
|
L.append(f" {DIM}hz_keys:{RST} "
|
||
|
|
+ " ".join(
|
||
|
|
f"{GREEN if float(i.get('score',0))>=0.9 else (YELLOW if float(i.get('score',0))>=0.5 else RED)}\u25cf{RST}{DIM}{k}{RST}"
|
||
|
|
for k, i in sorted(hz_ks.items())))
|
||
|
|
|
||
|
|
# LAST TRADES (from GREEN log)
|
||
|
|
if trades_hist:
|
||
|
|
L.append("")
|
||
|
|
L.append(f"{BW}{BOLD}LAST TRADES{RST} {DIM}(from GREEN log){RST}")
|
||
|
|
for t in trades_hist:
|
||
|
|
pnl = float(t.get("net_pnl", 0))
|
||
|
|
_not = float(t.get("notional", 0))
|
||
|
|
pct = (pnl / _not * 100) if _not else float(t.get("pnl_pct", 0)) * 100
|
||
|
|
lev = float(t.get("leverage", 0))
|
||
|
|
ep = float(t.get("entry_price", 0))
|
||
|
|
reason = t.get("reason", "?")
|
||
|
|
asset = t.get("asset", "?")
|
||
|
|
bars = t.get("bars_held", 0)
|
||
|
|
ts_raw = t.get("ts", "")[:16].replace("T", " ")
|
||
|
|
pc2 = GREEN if pnl >= 0 else RED
|
||
|
|
L.append(
|
||
|
|
f" {pc2}{'\u25b2' if pnl>=0 else '\u25bc'}{RST}"
|
||
|
|
f" {asset:<12} "
|
||
|
|
f"ep:{ep:.4g} "
|
||
|
|
f"lev:{lev:.2f}x "
|
||
|
|
f"pnl:{pc2}{pnl:+.2f}({pct:+.2f}%){RST} "
|
||
|
|
f"exit:{reason} bars:{bars} {DIM}{ts_raw}{RST}"
|
||
|
|
)
|
||
|
|
else:
|
||
|
|
L.append(f" {DIM}no completed trades yet (GREEN paper mode){RST}")
|
||
|
|
|
||
|
|
# LOG ERRORS
|
||
|
|
if log_errors:
|
||
|
|
L.append("")
|
||
|
|
L.append(f"{RED}{BOLD}LOG WARNINGS{RST}")
|
||
|
|
for e in log_errors:
|
||
|
|
L.append(f" {RED}{e[-120:]}{RST}")
|
||
|
|
|
||
|
|
# PNL HISTORY
|
||
|
|
try:
|
||
|
|
pnl_map = hz.get_map(PNL_MAP).blocking()
|
||
|
|
pnl_keys = sorted(pnl_map.key_set())
|
||
|
|
if pnl_keys:
|
||
|
|
L.append("")
|
||
|
|
L.append(f"{BW}{BOLD}PNL HISTORY{RST} {DIM}({PNL_MAP}){RST}")
|
||
|
|
for k in pnl_keys[-7:]:
|
||
|
|
v_raw = pnl_map.get(k)
|
||
|
|
if v_raw:
|
||
|
|
v = json.loads(v_raw)
|
||
|
|
ec = float(v.get("engine_capital", 0) or 0)
|
||
|
|
L.append(f" {DIM}{k}{RST} capital:${ec:,.2f}")
|
||
|
|
except Exception:
|
||
|
|
pass
|
||
|
|
|
||
|
|
L.append("")
|
||
|
|
L.append(f"{DIM}GREEN v1 \u2022 0.5s poll \u2022 CH\u2192status_snapshots \u2022 Ctrl-C quit{RST}")
|
||
|
|
|
||
|
|
# CH persistence
|
||
|
|
if int(time.time() * 2) % 2 == 0:
|
||
|
|
ch_put({
|
||
|
|
"ts": int(time.time() * 1000),
|
||
|
|
"strategy": STRATEGY,
|
||
|
|
"capital": capital,
|
||
|
|
"roi_pct": round(roi, 4),
|
||
|
|
"dd_pct": round(dd, 4),
|
||
|
|
"trades_executed": trades_ex,
|
||
|
|
"scans_processed": int(eng.get("scans_processed", 0) or 0),
|
||
|
|
"bar_idx": int(eng.get("bar_idx", 0) or 0),
|
||
|
|
"posture": posture,
|
||
|
|
"rm": round(rm, 6),
|
||
|
|
"vel_div": round(vel_div, 6),
|
||
|
|
"vol_ok": 1 if vol_ok else 0,
|
||
|
|
"phase": phase,
|
||
|
|
"mhs_status": mhs_st,
|
||
|
|
"boost": round(float(acb.get("boost", 1.0) if acb else 1.0), 4),
|
||
|
|
"cat5": round(float((safe.get("breakdown") or {}).get("Cat5", 1.0) or 1.0), 6),
|
||
|
|
"step_bar_ms": round(step_ms, 2),
|
||
|
|
})
|
||
|
|
|
||
|
|
return "\n".join(L)
|
||
|
|
|
||
|
|
|
||
|
|
def main():
|
||
|
|
print("Connecting to HZ...")
|
||
|
|
hz = hazelcast.HazelcastClient(
|
||
|
|
cluster_name="dolphin", cluster_members=["localhost:5701"],
|
||
|
|
connection_timeout=5.0)
|
||
|
|
print("Connected.\n")
|
||
|
|
try:
|
||
|
|
while True:
|
||
|
|
try:
|
||
|
|
sys.stdout.write(CLEAR + render(hz) + "\n")
|
||
|
|
sys.stdout.flush()
|
||
|
|
except Exception as e:
|
||
|
|
sys.stdout.write(f"\n{RED}render error: {e}{RST}\n")
|
||
|
|
sys.stdout.flush()
|
||
|
|
time.sleep(0.5)
|
||
|
|
except KeyboardInterrupt:
|
||
|
|
print(f"\n{DIM}Bye.{RST}")
|
||
|
|
finally:
|
||
|
|
hz.shutdown()
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|