Files
DOLPHIN/Observability/dolphin_status_v3.py
hjnormey 01c19662cb initial: import DOLPHIN baseline 2026-04-21 from dolphinng5_predict working tree
Includes core prod + GREEN/BLUE subsystems:
- prod/ (BLUE harness, configs, scripts, docs)
- nautilus_dolphin/ (GREEN Nautilus-native impl + dvae/ preserved)
- adaptive_exit/ (AEM engine + models/bucket_assignments.pkl)
- Observability/ (EsoF advisor, TUI, dashboards)
- external_factors/ (EsoF producer)
- mc_forewarning_qlabs_fork/ (MC regime/envelope)

Excludes runtime caches, logs, backups, and reproducible artifacts per .gitignore.
2026-04-21 16:58:38 +02:00

464 lines
17 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""DOLPHIN live status — v3
Polls HZ every 1s. Three gear rows: SIG / TRD / FIL (signal→fill path).
Run: source /home/dolphin/siloqy_env/bin/activate && python dolphin_status.py
Quit: Ctrl-C
"""
# v1 archived as dolphin_status_v1.py
# v2 archived inline (added SIG+TRD rows)
# v3: added FIL row — full signal→asset-pick→OBF→size→order visibility
import json, os, time, sys
from datetime import datetime, timezone
import hazelcast
CLEAR = "\033[2J\033[H"
BOLD = "\033[1m"; DIM = "\033[2m"; RST = "\033[0m"
GREEN = "\033[32m"; YELLOW = "\033[33m"; RED = "\033[31m"; CYAN = "\033[36m"
ORANGE = "\033[38;5;208m"
PC = {"APEX": GREEN, "STALKER": YELLOW, "TURTLE": ORANGE, "HIBERNATE": RED}
SC = {"GREEN": GREEN, "DEGRADED": YELLOW, "CRITICAL": ORANGE, "DEAD": RED}
# Thresholds from nautilus_event_trader.py
VEL_DIV_THRESHOLD = -0.020 # signal fires when vel_div < this
VEL_DIV_EXTREME = -0.050 # extreme bearish
VEL_DIV_WARN = -0.010 # approaching threshold (yellow)
VEL_DIV_CLOSE = -0.015 # nearly there (orange→yellow)
VOL_P60 = 0.00026414
START_CAP = None
CAP_PEAK = None
def _age(ts):
if not ts: return "?"
s = time.time() - ts
if s < 0: return "0s"
if s < 60: return f"{s:.0f}s"
if s < 3600: return f"{s/60:.0f}m"
return f"{s/3600:.1f}h"
def _bar(v, w=20):
v = max(0.0, min(1.0, v))
return "" * round(v * w) + "" * (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 {}
# ── Gear items ────────────────────────────────────────────────────────────────
# Each returns (label, color, value_str)
def _item(label, color, val=""):
dot = f"{color}{RST}"
v = f":{val}" if val else ""
return f"{dot}{DIM}{label}{v}{RST}"
def _vel_item(vel_div):
"""vel_div colored by distance to threshold (-0.02)."""
v = f"{vel_div:+.4f}"
if vel_div <= VEL_DIV_EXTREME:
return _item("vel_div", GREEN, v) # extremely bearish — great
elif vel_div <= VEL_DIV_THRESHOLD:
return _item("vel_div", GREEN, v) # past threshold — signal green
elif vel_div <= VEL_DIV_CLOSE:
return _item("vel_div", YELLOW, v) # -0.015 to -0.020 — close
elif vel_div <= VEL_DIV_WARN:
return _item("vel_div", ORANGE, v) # -0.010 to -0.015 — approaching
elif vel_div < 0:
return _item("vel_div", RED, v) # negative but far
else:
return _item("vel_div", RED, v) # positive — not bearish
def signal_fired(vel_div, vol_ok, posture, acb_ready, exf_ok, halt):
"""True if ALL signal preconditions are green."""
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 # no open position already
and lev < abs_cap # leverage headroom
and daily_loss_ok
and boost > 0
)
OB_IMBALANCE_BIAS = -0.09 # from engine config: ob_imbalance_bias
def _best_fill_candidate(obf_universe):
"""Pick best SHORT candidate from OBF universe.
Criteria: negative imbalance (bearish pressure) + high fill_probability + low spread.
Returns (symbol, asset_dict) or (None, {}).
"""
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, {}
# Score: fill_prob * (1 + bearish_imbalance_bonus) / (1 + spread_bps/10)
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))
# Bearish bias: reward negative imbalance, penalise positive
imb_bonus = max(0.0, -imb) # 0..1 for imbalance in [-1,0]
return fp * (1 + imb_bonus) * dq / max(0.1, sp)
candidates.sort(key=score, reverse=True)
return candidates[0]
def fill_row(obf_universe, acb, eng):
"""Row 3: signal → asset-pick → OBF liquidity → size → ORDER."""
f_items = []
# ── Asset picker (IRP/ARS) ─────────────────────────────────────────────
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)
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))
# Best candidate asset
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]))
# OBF: fill probability
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}"))
# OBF: spread
f_items.append(_item("spread",
GREEN if spread <= 3 else (YELLOW if spread <= 8 else RED),
f"{spread:.1f}bps"))
# OBF: depth quality
f_items.append(_item("depth_q",
GREEN if dq >= 0.5 else (YELLOW if dq >= 0.1 else RED),
f"{dq:.2f}"))
# OBF: imbalance direction (SHORT needs bearish = negative)
imb_ok = imb < OB_IMBALANCE_BIAS # confirmed bearish pressure
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}"))
# OBF: depth USD
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"))
# ── Sizing — ACB boost × proxy_B prank ────────────────────────────────
# proxy_B prank not exposed in HZ snapshot; show ACB boost as sizing proxy
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"×{boost:.2f}"))
f_items.append(_item("beta",
GREEN if beta >= 0.7 else (YELLOW if beta >= 0.4 else RED),
f"{beta:.2f}"))
# ── ORDER indicator ────────────────────────────────────────────────────
# Would an order fire if signal were green right now?
open_count = len(eng.get("open_positions") or [])
lev = float(eng.get("current_leverage", 0) or 0)
abs_c = float(eng.get("leverage_abs_cap", 9.0) or 9.0)
order_ready = (
sym is not None
and fill_p >= 0.60
and open_count == 0
and lev < abs_c
and boost > 0
) if sym else False
if order_ready:
f_items.append(f" {CYAN}{BOLD}◉ ORDER READY{RST}")
else:
f_items.append(f" {DIM}(order: waiting){RST}")
return " ".join(f_items)
def gear_rows(eng, safe, acb, exf, hb, obf_universe=None):
"""Return three formatted rows: SIGNAL, TRADE gates, FILL path."""
vel_div = float(eng.get("last_vel_div", 0) or 0)
vol_ok = bool(eng.get("vol_ok", False))
posture = safe.get("posture") or eng.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 # cut=0 means blocked
exf_ok_count = int(exf.get("_ok_count", 0) if exf else 0)
exf_ok = exf_ok_count >= 3
open_count = len(eng.get("open_positions") or [])
lev = float(eng.get("current_leverage", 0) or 0)
abs_cap = float(eng.get("leverage_abs_cap", 9.0) or 9.0)
trades_ex = int(eng.get("trades_executed") or 0)
hb_ts = hb.get("ts")
hb_ok = bool(hb_ts and (time.time() - hb_ts) < 30)
# ── SIGNAL ROW ────────────────────────────────────────────────────────────
# Preconditions for the engine to generate a signal
s_items = []
s_items.append(_vel_item(vel_div))
# vol_ok — BTC vol above p60
s_items.append(_item("vol_ok",
GREEN if vol_ok else RED,
"" if vol_ok else ""))
# posture gate
pc = PC.get(posture, DIM)
posture_ok = posture in ("APEX", "STALKER")
s_items.append(_item("posture",
GREEN if posture == "APEX" else (YELLOW if posture == "STALKER" else RED),
posture))
# acb_ready
s_items.append(_item("acb",
GREEN if acb_ready else (ORANGE if acb_boost_val > 0 else RED),
f"{acb_boost_val:.2f}"))
# exf_ok — external factors pipeline
s_items.append(_item("exf",
GREEN if exf_ok else (YELLOW if exf_ok_count >= 1 else RED),
f"{exf_ok_count}/5"))
# halt gate
s_items.append(_item("no_halt",
GREEN if not halt else RED,
"" if not halt else "HALT"))
# heartbeat
s_items.append(_item("hb",
GREEN if hb_ok else RED,
_age(hb_ts)))
# ALL GREEN → fire indicator
all_sig = signal_fired(vel_div, vol_ok, posture, acb_ready, exf_ok, halt)
if all_sig:
s_items.append(f" {GREEN}{BOLD}◉ SIGNAL{RST}")
# ── TRADE ROW ─────────────────────────────────────────────────────────────
# Additional gates that must pass before a matched signal becomes a fill
t_items = []
# open positions
t_items.append(_item("open_pos",
GREEN if open_count == 0 else ORANGE,
str(open_count)))
# leverage headroom
lev_pct = lev / abs_cap if abs_cap else 0
t_items.append(_item("lev",
GREEN if lev_pct < 0.3 else (YELLOW if lev_pct < 0.7 else RED),
f"{lev:.2f}x/{abs_cap:.0f}"))
# regime_dd_halt
t_items.append(_item("regime",
GREEN if not halt else RED,
"free" if not halt else "HALTED"))
# Rm strength
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}"))
# Cat5 (intraday drawdown contribution)
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}"))
# trades today
t_items.append(_item("trades",
GREEN if trades_ex < 20 else (YELLOW if trades_ex < 35 else ORANGE),
str(trades_ex)))
# ALL GREEN trade execute indicator
daily_loss_ok = c5 > 0.50 # reasonable proxy — Cat5 tracks drawdown
all_trade = all_sig and trade_can_execute(open_count, lev, abs_cap, daily_loss_ok, acb_boost_val)
if all_trade:
t_items.append(f" {CYAN}{BOLD}◉ TRADE{RST}")
sig_row = " ".join(s_items)
trade_row = " ".join(t_items)
fill = fill_row(obf_universe or {}, acb, eng)
return sig_row, trade_row, fill
def render(hz):
global START_CAP, CAP_PEAK
eng = _get(hz, "DOLPHIN_STATE_BLUE", "engine_snapshot")
cap = _get(hz, "DOLPHIN_STATE_BLUE", "capital_checkpoint")
safe = _get(hz, "DOLPHIN_SAFETY", "latest")
hb = _get(hz, "DOLPHIN_HEARTBEAT", "nautilus_flow_heartbeat")
mh = _get(hz, "DOLPHIN_META_HEALTH", "latest")
acb = _get(hz, "DOLPHIN_FEATURES", "acb_boost")
exf = _get(hz, "DOLPHIN_FEATURES", "exf_latest")
obf = _get(hz, "DOLPHIN_FEATURES", "obf_universe_latest")
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))
hb_ts = hb.get("ts")
phase = hb.get("phase", "?")
trader_up = hb_ts and (time.time() - hb_ts) < 30
trades = eng.get("trades_executed", "")
scans = eng.get("scans_processed", "")
lev = float(eng.get("current_leverage", 0))
notional= float(eng.get("open_notional", 0))
mhs_st = mh.get("status", "?")
rm_meta = float(mh.get("rm_meta", 0))
if capital > 0:
if START_CAP is None: START_CAP = capital
if CAP_PEAK is None or capital > CAP_PEAK: CAP_PEAK = capital
roi = ((capital - START_CAP) / START_CAP * 100) if START_CAP and capital else 0
dd = ((CAP_PEAK - capital) / CAP_PEAK * 100) if CAP_PEAK and capital < CAP_PEAK else 0
pc = PC.get(posture, DIM)
sc = SC.get(mhs_st, DIM)
tc = GREEN if trader_up else RED
roi_c = GREEN if roi >= 0 else RED
dd_c = RED if dd > 15 else (YELLOW if dd > 5 else GREEN)
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 = []
L.append(f"{BOLD}{CYAN}🐬 DOLPHIN-NAUTILUS{RST} {DIM}{now}{RST}")
L.append("" * 60)
# TRADER
L.append(f"{BOLD}TRADER{RST} {tc}{'● LIVE' if trader_up else '● DOWN'}{RST}"
f" phase:{phase} hb:{_age(hb_ts)}"
f" scan:#{eng.get('last_scan_number','?')}")
# ── SIGNAL → FILL GEARS ───────────────────────────────────────────────────
L.append(f" {DIM}SIG │{RST} {sig_row}")
L.append(f" {DIM}TRD │{RST} {trade_row}")
L.append(f" {DIM}FIL │{RST} {fill_row_str}")
L.append("")
# CAPITAL
L.append(f"{BOLD}CAPITAL{RST} {CYAN}${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} scans:{scans} bar:{eng.get('bar_idx','?')}"
f" lev:{lev:.2f}x notional:${notional:,.0f}")
# Open positions
positions = eng.get("open_positions") or []
if positions:
L.append(f" {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
bd = safe.get("breakdown") or {}
L.append(f"{BOLD}POSTURE{RST} {pc}{posture}{RST} Rm:{pc}{_bar(rm,20)}{RST} {rm:.4f}")
cats = " ".join(f"C{i}:{float(bd.get(f'Cat{i}',0)):.2f}" for i in range(1,6))
L.append(f" {cats} f_env:{float(bd.get('f_env',0)):.3f} f_exe:{float(bd.get('f_exe',0)):.3f}")
L.append("")
# SYS HEALTH
L.append(f"{BOLD}SYS HEALTH{RST} {sc}{mhs_st}{RST} rm_meta:{rm_meta:.3f}")
for m in ("m1_data_infra","m1_trader","m2_heartbeat",
"m3_data_freshness","m4_control_plane","m5_coherence"):
v = float(mh.get(m, 0))
c = GREEN if v >= 0.9 else (YELLOW if v >= 0.5 else RED)
L.append(f" {c}{m}:{v:.3f}{RST}")
L.append(f" {DIM}services:{RST} "
+ " ".join(
f"{'' if st=='RUNNING' else f'{RED}{RST}'}{DIM}{n.split(':')[-1]}{RST}"
if st == "RUNNING" else
f"{RED}{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)}{RST}{DIM}{k}{RST}"
for k, i in sorted(hz_ks.items())))
L.append("")
L.append(f"{DIM}v3 • 1s poll • Ctrl-C quit{RST}")
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(1)
except KeyboardInterrupt:
print(f"\n{DIM}Bye.{RST}")
finally:
hz.shutdown()
if __name__ == "__main__":
main()