Monitor that shows analyst also. Analyst that controlls its logging.
This commit is contained in:
@@ -5,7 +5,7 @@ import json
|
||||
import time
|
||||
import os
|
||||
from datetime import datetime
|
||||
from collections import deque # Added for history tracking
|
||||
from collections import deque
|
||||
|
||||
from rich.live import Live
|
||||
from rich.table import Table
|
||||
@@ -16,8 +16,13 @@ from rich.text import Text
|
||||
|
||||
# --- CONFIGURATION ---
|
||||
INPUT_SOCKET = "/tmp/streamer.sock"
|
||||
|
||||
ONRAMP_HOST = "127.0.0.1"
|
||||
ONRAMP_PORT = 9999
|
||||
|
||||
ANALYST_HOST = "127.0.0.1"
|
||||
ANALYST_PORT = 9997
|
||||
|
||||
REFRESH_RATE = 1.0
|
||||
|
||||
# Global state to track lag history (last 300 seconds)
|
||||
@@ -25,6 +30,8 @@ LAG_HISTORY = deque(maxlen=300)
|
||||
|
||||
console = Console()
|
||||
|
||||
# ------------------- QUERY HELPERS -------------------
|
||||
|
||||
def query_input_go():
|
||||
if not os.path.exists(INPUT_SOCKET):
|
||||
return None
|
||||
@@ -36,12 +43,13 @@ def query_input_go():
|
||||
except:
|
||||
return None
|
||||
|
||||
def query_onramp(command):
|
||||
|
||||
def query_tcp_json(host, port, payload=b"status"):
|
||||
try:
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.settimeout(1.0)
|
||||
s.connect((ONRAMP_HOST, ONRAMP_PORT))
|
||||
s.sendall(command.encode('utf-8'))
|
||||
s.connect((host, port))
|
||||
s.sendall(payload)
|
||||
|
||||
chunks = []
|
||||
while True:
|
||||
@@ -50,11 +58,13 @@ def query_onramp(command):
|
||||
break
|
||||
chunks.append(chunk)
|
||||
|
||||
full_data = b"".join(chunks).decode('utf-8')
|
||||
return json.loads(full_data)
|
||||
return json.loads(b"".join(chunks).decode())
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
# ------------------- LAYOUT -------------------
|
||||
|
||||
def make_layout():
|
||||
layout = Layout()
|
||||
layout.split(
|
||||
@@ -66,35 +76,38 @@ def make_layout():
|
||||
layout["main"].split_row(
|
||||
Layout(name="input_svc"),
|
||||
Layout(name="onramp_svc"),
|
||||
Layout(name="analyst_svc"),
|
||||
)
|
||||
return layout
|
||||
|
||||
|
||||
# ------------------- PANELS -------------------
|
||||
|
||||
def get_input_panel():
|
||||
raw = query_input_go()
|
||||
if not raw:
|
||||
return Panel(Text("OFFLINE", style="bold red"), title="[1] Input Service (Go)", border_style="red")
|
||||
return Panel(Text("OFFLINE", style="bold red"),
|
||||
title="[1] Input Service (Go)", border_style="red")
|
||||
parts = raw.split("|")
|
||||
content = "\n".join([p.strip() for p in parts])
|
||||
return Panel(content, title="[1] Input Service (Go)", border_style="green")
|
||||
|
||||
def get_onramp_panel():
|
||||
data = query_onramp("status")
|
||||
if not data:
|
||||
LAG_HISTORY.clear() # Clear history if service goes down
|
||||
return Panel(Text("OFFLINE", style="bold red"), title="[2] Onramp Service (Go)", border_style="red")
|
||||
|
||||
# 1. Calculate Instant Lag
|
||||
def get_onramp_panel():
|
||||
data = query_tcp_json(ONRAMP_HOST, ONRAMP_PORT)
|
||||
if not data:
|
||||
LAG_HISTORY.clear()
|
||||
return Panel(Text("OFFLINE", style="bold red"),
|
||||
title="[2] Onramp Service (Go)", border_style="red")
|
||||
|
||||
last_ts = data.get('last_ts', 0) / 1000
|
||||
current_lag = time.time() - last_ts if last_ts > 0 else 0
|
||||
|
||||
# 2. Update History
|
||||
LAG_HISTORY.append(current_lag)
|
||||
|
||||
# 3. Calculate Averages (Load Average style)
|
||||
avg_1m = sum(list(LAG_HISTORY)[-60:]) / min(len(LAG_HISTORY), 60)
|
||||
avg_5m = sum(LAG_HISTORY) / len(LAG_HISTORY)
|
||||
|
||||
# 4. Determine Styling
|
||||
lag_style = "green" if current_lag < 2 else "yellow" if current_lag < 5 else "bold red"
|
||||
|
||||
content = Text()
|
||||
@@ -102,17 +115,53 @@ def get_onramp_panel():
|
||||
content.append(f"Total Trades : {data.get('total_trades')}\n")
|
||||
content.append(f"Current File : {os.path.basename(str(data.get('last_file')))}\n")
|
||||
|
||||
# The "Load Average" line
|
||||
content.append("Lag (Avg) : ", style="white")
|
||||
content.append("Lag (Avg) : ")
|
||||
content.append(f"{current_lag:.2f}s", style=lag_style)
|
||||
content.append(f", {avg_1m:.2f}s/1m", style="dim" if avg_1m < 2 else "yellow")
|
||||
content.append(f", {avg_5m:.2f}s/5m", style="dim" if avg_5m < 2 else "yellow")
|
||||
|
||||
return Panel(content, title="[2] Onramp Service (Go)", border_style="blue")
|
||||
|
||||
|
||||
def get_analyst_panel():
|
||||
data = query_tcp_json(ANALYST_HOST, ANALYST_PORT)
|
||||
if not data:
|
||||
return Panel(Text("OFFLINE", style="bold red"),
|
||||
title="[3] Analyst Service (Python)", border_style="red")
|
||||
|
||||
tf_data = data.get("timeframes", {})
|
||||
active = ", ".join(data.get("active_timeframes", []))
|
||||
|
||||
content = Text()
|
||||
content.append(f"Active TFs : {active}\n\n")
|
||||
|
||||
for tf in ["1m", "5m", "15m", "1h"]:
|
||||
tf_info = tf_data.get(tf)
|
||||
if not tf_info:
|
||||
continue
|
||||
|
||||
last_ts = tf_info.get("last")
|
||||
last_str = (
|
||||
datetime.fromtimestamp(last_ts).strftime('%H:%M:%S')
|
||||
if last_ts else "n/a"
|
||||
)
|
||||
|
||||
new = tf_info.get("new", 0)
|
||||
color = "green" if new > 0 else "dim"
|
||||
|
||||
content.append(f"{tf:>4} | ")
|
||||
content.append(f"+{new:<4}", style=color)
|
||||
content.append(f" last: {last_str}\n")
|
||||
|
||||
return Panel(content, title="[3] Analyst Service (Python)", border_style="magenta")
|
||||
|
||||
|
||||
# ------------------- MARKET TABLE -------------------
|
||||
|
||||
def get_market_table():
|
||||
res = query_onramp("live")
|
||||
res = query_tcp_json(ONRAMP_HOST, ONRAMP_PORT, b"live")
|
||||
table = Table(expand=True, border_style="cyan", header_style="bold cyan")
|
||||
|
||||
table.add_column("TF", justify="center", style="bold yellow")
|
||||
table.add_column("Last Update", justify="center")
|
||||
table.add_column("Open", justify="right")
|
||||
@@ -136,24 +185,49 @@ def get_market_table():
|
||||
buy_color = "green" if buy_pct > 50 else "red"
|
||||
|
||||
table.add_row(
|
||||
tf, ts_str, f"{c['open']:.2f}", f"{c['high']:.2f}", f"{c['low']:.2f}",
|
||||
tf, ts_str,
|
||||
f"{c['open']:.2f}", f"{c['high']:.2f}", f"{c['low']:.2f}",
|
||||
Text(f"{c['close']:.2f}", style=f"bold {color}"),
|
||||
f"{c['volume']:.2f}", Text(f"{buy_pct:.1f}%", style=buy_color)
|
||||
f"{c['volume']:.2f}",
|
||||
Text(f"{buy_pct:.1f}%", style=buy_color)
|
||||
)
|
||||
else:
|
||||
table.add_row("waiting...", "-", "-", "-", "-", "-", "-", "-")
|
||||
|
||||
return table
|
||||
|
||||
|
||||
# ------------------- MAIN -------------------
|
||||
|
||||
def main():
|
||||
layout = make_layout()
|
||||
with Live(layout, refresh_per_second=2, screen=True):
|
||||
while True:
|
||||
layout["header"].update(Panel(Text(f"BYBIT BTCUSDT UNIFIED MONITOR | {datetime.now().strftime('%H:%M:%S')}", justify="center", style="bold white on blue")))
|
||||
layout["header"].update(
|
||||
Panel(
|
||||
Text(
|
||||
f"BYBIT BTCUSDT UNIFIED MONITOR | {datetime.now().strftime('%H:%M:%S')}",
|
||||
justify="center",
|
||||
style="bold white on blue"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
layout["input_svc"].update(get_input_panel())
|
||||
layout["onramp_svc"].update(get_onramp_panel())
|
||||
layout["analyst_svc"].update(get_analyst_panel())
|
||||
layout["market"].update(get_market_table())
|
||||
layout["footer"].update(Text("Press Ctrl+C to exit | Monitoring: publicTrade.BTCUSDT", justify="center", style="dim"))
|
||||
|
||||
layout["footer"].update(
|
||||
Text(
|
||||
"Ctrl+C to exit | Pipeline: Input → Onramp → Analyst",
|
||||
justify="center",
|
||||
style="dim"
|
||||
)
|
||||
)
|
||||
|
||||
time.sleep(REFRESH_RATE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user