Files
hyper/live_market_utils.py
2025-10-25 21:51:25 +02:00

107 lines
4.0 KiB
Python

import logging
import json
import time
import os
import traceback
from hyperliquid.info import Info
from hyperliquid.utils import constants
from logging_utils import setup_logging
# --- Configuration for standalone error logging ---
LOGS_DIR = "_logs"
ERROR_LOG_FILE = os.path.join(LOGS_DIR, "live_market_errors.log")
def log_error(error_message: str, include_traceback: bool = True):
"""A simple, robust file logger for any errors."""
try:
if not os.path.exists(LOGS_DIR):
os.makedirs(LOGS_DIR)
with open(ERROR_LOG_FILE, 'a') as f:
timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime())
f.write(f"--- ERROR at {timestamp} UTC ---\n")
f.write(error_message + "\n")
if include_traceback:
f.write(traceback.format_exc() + "\n")
f.write("="*50 + "\n")
except Exception:
print(f"CRITICAL: Failed to write to error log file: {error_message}", file=sys.stderr)
def on_message(message, shared_prices_dict):
"""
Callback function to process incoming 'allMids' messages and update the
shared memory dictionary directly.
"""
try:
if message.get("channel") == "allMids":
new_prices = message.get("data", {}).get("mids", {})
shared_prices_dict.update(new_prices)
except Exception as e:
log_error(f"Error in WebSocket on_message: {e}")
def start_live_feed(shared_prices_dict, log_level='off'):
"""
Main function for the WebSocket process. It takes a shared dictionary
and continuously feeds it with live market data.
Includes a watchdog to auto-reconnect on failure.
"""
setup_logging(log_level, 'LiveMarketFeed')
info = None
callback = lambda msg: on_message(msg, shared_prices_dict)
def connect_and_subscribe():
"""Establishes a new WebSocket connection and subscribes to allMids."""
try:
logging.info("Connecting to Hyperliquid WebSocket...")
# Ensure skip_ws=False to create the ws_manager
new_info = Info(constants.MAINNET_API_URL, skip_ws=False)
subscription = {"type": "allMids"}
new_info.subscribe(subscription, callback)
logging.info("WebSocket connected and subscribed to 'allMids'.")
return new_info
except Exception as e:
log_error(f"Failed to connect to WebSocket: {e}")
return None
info = connect_and_subscribe()
logging.info("Starting live price feed process. Press Ctrl+C in main app to stop.")
try:
while True:
# --- Watchdog Logic ---
time.sleep(15) # Check the connection every 15 seconds
# --- FIX: Changed 'is_running()' to the correct method 'is_alive()' ---
if info is None or not info.ws_manager.is_alive():
error_msg = "WebSocket connection lost or not running. Attempting to reconnect..."
logging.warning(error_msg)
log_error(error_msg, include_traceback=False) # Log it to the file
if info and info.ws_manager: # Check if ws_manager exists before stopping
try:
info.ws_manager.stop() # Clean up old manager
except Exception as e:
log_error(f"Error stopping old ws_manager: {e}")
info = connect_and_subscribe()
if info is None:
logging.error("Reconnect failed, will retry in 15s.")
else:
logging.info("Successfully reconnected to WebSocket.")
else:
logging.debug("Watchdog check: WebSocket connection is active.")
except KeyboardInterrupt:
logging.info("Stopping WebSocket listener...")
except Exception as e:
log_error(f"Live Market Feed process crashed: {e}")
finally:
if info and info.ws_manager:
info.ws_manager.stop()
logging.info("Listener stopped.")