Bug fixes in input and onramp. Hot config reload on signals. Added example utility scripts for signals.

This commit is contained in:
Kalzu Rekku
2026-01-17 14:47:13 +02:00
parent 7d7038d6bd
commit aa216981d2
16 changed files with 2339 additions and 306 deletions

View File

@@ -16,6 +16,7 @@ from rich.text import Text
# --- CONFIGURATION ---
INPUT_SOCKET = "/tmp/streamer.sock"
SIGNALS_HEALTH_SOCKET = "/tmp/signals_health.sock"
ONRAMP_HOST = "127.0.0.1"
ONRAMP_PORT = 9999
@@ -44,6 +45,24 @@ def query_input_go():
return None
def query_signals_health():
if not os.path.exists(SIGNALS_HEALTH_SOCKET):
return None
try:
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
s.settimeout(0.5)
s.connect(SIGNALS_HEALTH_SOCKET)
chunks = []
while True:
chunk = s.recv(4096)
if not chunk:
break
chunks.append(chunk)
return json.loads(b"".join(chunks).decode())
except:
return None
def query_tcp_json(host, port, payload=b"status"):
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
@@ -69,14 +88,23 @@ def make_layout():
layout = Layout()
layout.split(
Layout(name="header", size=3),
Layout(name="main", size=10),
Layout(name="services", size=18),
Layout(name="signals_table", size=8),
Layout(name="market", size=12),
Layout(name="footer", size=3),
)
layout["main"].split_row(
# Split services into 2x2 grid
layout["services"].split_column(
Layout(name="services_row1"),
Layout(name="services_row2"),
)
layout["services_row1"].split_row(
Layout(name="input_svc"),
Layout(name="onramp_svc"),
)
layout["services_row2"].split_row(
Layout(name="analyst_svc"),
Layout(name="signals_svc"),
)
return layout
@@ -156,6 +184,93 @@ def get_analyst_panel():
return Panel(content, title="[3] Analyst Service (Python)", border_style="magenta")
def get_signals_panel():
data = query_signals_health()
if not data:
return Panel(Text("OFFLINE", style="bold red"),
title="[4] Signal Generator (Python)", border_style="red")
status_color = "green" if data.get("status") == "running" else "red"
content = Text()
content.append(f"Status : ")
content.append(f"{data.get('status', 'unknown').upper()}", style=f"bold {status_color}")
content.append(f"\nPersonality : {data.get('personality', 'n/a')}\n")
content.append(f"Timeframes : {', '.join(data.get('timeframes', []))}\n")
content.append(f"Uptime : {data.get('uptime_seconds', 0)}s ({data.get('uptime_seconds', 0)//60}m)\n")
total = data.get('total_signals', 0)
buy = data.get('buy_signals', 0)
sell = data.get('sell_signals', 0)
content.append(f"Total Sigs : {total} (")
content.append(f"{buy}", style="green")
content.append(" / ")
content.append(f"{sell}", style="red")
content.append(")\n")
content.append(f"Errors : {data.get('errors', 0)}\n")
content.append(f"Clients : {data.get('connected_clients', 0)}\n")
last_sig = data.get('last_signal')
if last_sig:
try:
last_time = datetime.fromisoformat(last_sig.replace('Z', '+00:00'))
time_ago = (datetime.now(last_time.tzinfo) - last_time).total_seconds()
content.append(f"Last Signal : {int(time_ago)}s ago")
except:
content.append(f"Last Signal : {last_sig}")
else:
content.append(f"Last Signal : None")
border_color = "green" if data.get("status") == "running" else "red"
return Panel(content, title="[4] Signal Generator (Python)", border_style=border_color)
# ------------------- SIGNALS TABLE -------------------
def get_signals_table():
data = query_signals_health()
table = Table(expand=True, border_style="yellow", header_style="bold yellow")
table.add_column("Time", justify="center", style="dim")
table.add_column("TF", justify="center")
table.add_column("Signal", justify="center", style="bold")
table.add_column("Price", justify="right")
table.add_column("Conf", justify="right")
table.add_column("Reason", justify="left", no_wrap=False)
if data and "recent_signals" in data and data["recent_signals"]:
for sig in data["recent_signals"][-5:]: # Last 5 signals
try:
sig_time = datetime.fromisoformat(sig['generated_at'].replace('Z', '+00:00'))
time_str = sig_time.strftime('%H:%M:%S')
except:
time_str = "n/a"
signal_type = sig.get('signal', '?')
signal_color = "green" if signal_type == "BUY" else "red"
# Take first 2 reasons
reasons = sig.get('reasons', [])
reason_str = ", ".join(reasons[:2])
if len(reasons) > 2:
reason_str += "..."
table.add_row(
time_str,
sig.get('timeframe', '?'),
Text(signal_type, style=f"bold {signal_color}"),
f"${sig.get('price', 0):.2f}",
f"{sig.get('confidence', 0)*100:.0f}%",
reason_str
)
else:
table.add_row("", "", "", "", "", "No signals yet")
return Panel(table, title="Recent Signals", border_style="yellow")
# ------------------- MARKET TABLE -------------------
def get_market_table():
@@ -194,39 +309,47 @@ def get_market_table():
else:
table.add_row("waiting...", "-", "-", "-", "-", "-", "-", "-")
return table
return Panel(table, title="Live Market Data", border_style="cyan")
# ------------------- 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"
try:
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["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["input_svc"].update(get_input_panel())
layout["onramp_svc"].update(get_onramp_panel())
layout["analyst_svc"].update(get_analyst_panel())
layout["signals_svc"].update(get_signals_panel())
layout["signals_table"].update(get_signals_table())
layout["market"].update(get_market_table())
layout["footer"].update(
Text(
"Ctrl+C to exit | Pipeline: Input → Onramp → Analyst",
justify="center",
style="dim"
layout["footer"].update(
Text(
"Ctrl+C to exit | Pipeline: Input → Onramp → Analyst → Signals",
justify="center",
style="dim"
)
)
)
time.sleep(REFRESH_RATE)
time.sleep(REFRESH_RATE)
except KeyboardInterrupt:
console.print("\n[yellow]Shutting down monitor...[/yellow]")
finally:
console.print("[green]Monitor stopped.[/green]")
if __name__ == "__main__":