live market websocket and monitoring wallets

This commit is contained in:
2025-10-20 20:46:48 +02:00
parent 64f7866083
commit 70f3d48336
13 changed files with 996 additions and 183 deletions

258
live_market.py Normal file
View File

@ -0,0 +1,258 @@
import os
import sys
import time
import json
import argparse
from datetime import datetime, timedelta, timezone
from hyperliquid.info import Info
from hyperliquid.utils import constants
from collections import deque, defaultdict
# --- Configuration ---
MAX_TRADE_HISTORY = 100000
all_trades = {
"BTC": deque(maxlen=MAX_TRADE_HISTORY),
"ETH": deque(maxlen=MAX_TRADE_HISTORY),
}
latest_raw_trades = {
"BTC": None,
"ETH": None,
}
decoded_trade_output = []
_lines_printed = 0
def get_coins_from_strategies() -> set:
"""
Reads the strategies.json file and returns a unique set of coin symbols
from all enabled strategies.
"""
coins = set()
config_path = os.path.join("_data", "strategies.json")
try:
with open(config_path, 'r') as f:
all_configs = json.load(f)
for name, config in all_configs.items():
if config.get("enabled", False):
coin = config.get("parameters", {}).get("coin")
if coin:
coins.add(coin)
print(f"Found {len(coins)} unique coins to watch from enabled strategies: {list(coins)}")
return coins
except (FileNotFoundError, json.JSONDecodeError) as e:
print(f"ERROR: Could not load or parse '{config_path}': {e}", file=sys.stderr)
return set()
def on_message(message):
"""
Callback function to process incoming trades from the WebSocket and store them.
"""
try:
if message.get("channel") == "trades":
for trade in message["data"]:
coin = trade['coin']
if coin in all_trades:
latest_raw_trades[coin] = trade
price = float(trade['px'])
size = float(trade['sz'])
decoded_trade = {
"time": datetime.fromtimestamp(trade['time'] / 1000, tz=timezone.utc),
"side": "BUY" if trade['side'] == "B" else "SELL",
"value": price * size,
"users": trade.get('users', [])
}
all_trades[coin].append(decoded_trade)
except (KeyError, TypeError, ValueError):
pass
def build_top_trades_table(title: str, trades: list) -> list:
"""Builds the formatted lines for a top-5 trades by value table."""
lines = []
header = f"{'Time (UTC)':<10} | {'Side':<5} | {'Value (USD)':>20}"
lines.append(f"--- {title} ---")
lines.append(header)
lines.append("-" * len(header))
top_trades = sorted(trades, key=lambda x: x['value'], reverse=True)[:5]
for trade in top_trades:
lines.append(
f"{trade['time'].strftime('%H:%M:%S'):<10} | "
f"{trade['side']:<5} | "
f"${trade['value']:>18,.2f}"
)
while len(lines) < 8: lines.append(" " * len(header))
return lines
def build_top_takers_table(title: str, trades: list) -> list:
"""Analyzes a list of trades to find the top 5 takers by total volume."""
lines = []
header = f"{'#':<2} | {'Taker Address':<15} | {'Total Volume (USD)':>20}"
lines.append(f"--- {title} ---")
lines.append(header)
lines.append("-" * len(header))
volumes = defaultdict(float)
for trade in trades:
for user in trade['users']:
volumes[user] += trade['value']
top_takers = sorted(volumes.items(), key=lambda item: item[1], reverse=True)[:5]
for i, (address, volume) in enumerate(top_takers, 1):
short_address = f"{address[:6]}...{address[-4:]}"
lines.append(f"{i:<2} | {short_address:<15} | ${volume:>18,.2f}")
while len(lines) < 8: lines.append(" " * len(header))
return lines
def build_top_active_takers_table(title: str, trades: list) -> list:
"""Analyzes a list of trades to find the top 5 takers by trade count."""
lines = []
header = f"{'#':<2} | {'Taker Address':<42} | {'Trade Count':>12} | {'Total Volume (USD)':>20}"
lines.append(f"--- {title} ---")
lines.append(header)
lines.append("-" * len(header))
taker_data = defaultdict(lambda: {'count': 0, 'volume': 0.0})
for trade in trades:
for user in trade['users']:
taker_data[user]['count'] += 1
taker_data[user]['volume'] += trade['value']
top_takers = sorted(taker_data.items(), key=lambda item: item[1]['count'], reverse=True)[:5]
for i, (address, data) in enumerate(top_takers, 1):
lines.append(f"{i:<2} | {address:<42} | {data['count']:>12} | ${data['volume']:>18,.2f}")
while len(lines) < 8: lines.append(" " * len(header))
return lines
def build_decoded_trade_lines(coin: str) -> list:
"""Builds a formatted, multi-line string for a single decoded trade."""
trade = latest_raw_trades[coin]
if not trade: return ["No trade data yet..."] * 7
return [
f"Time: {datetime.fromtimestamp(trade['time'] / 1000, tz=timezone.utc)}",
f"Side: {'BUY' if trade.get('side') == 'B' else 'SELL'}",
f"Price: {trade.get('px', 'N/A')}",
f"Size: {trade.get('sz', 'N/A')}",
f"Trade ID: {trade.get('tid', 'N/A')}",
f"Hash: {trade.get('hash', 'N/A')}",
f"Users: {', '.join(trade.get('users', []))}"
]
def update_decoded_trade_display():
"""
Updates the global variable holding the decoded trade output, but only
at the 40-second mark of each minute.
"""
global decoded_trade_output
if datetime.now().second == 40:
lines = []
lines.append("--- Last BTC Trade (Decoded) ---")
lines.extend(build_decoded_trade_lines("BTC"))
lines.append("")
lines.append("--- Last ETH Trade (Decoded) ---")
lines.extend(build_decoded_trade_lines("ETH"))
decoded_trade_output = lines
def display_dashboard(view: str):
"""Clears the screen and prints the selected dashboard view."""
global _lines_printed
if _lines_printed > 0: print(f"\x1b[{_lines_printed}A", end="")
now_utc = datetime.now(timezone.utc)
output_lines = []
separator = " | "
time_windows = [
("All Time", None), ("Last 24h", timedelta(hours=24)),
("Last 1h", timedelta(hours=1)), ("Last 5m", timedelta(minutes=5)),
("Last 1m", timedelta(minutes=1)),
]
btc_trades_copy = list(all_trades["BTC"])
eth_trades_copy = list(all_trades["ETH"])
if view == "trades":
output_lines.append("--- Top 5 Trades by Value ---")
for title, delta in time_windows:
btc_trades = [t for t in btc_trades_copy if not delta or t['time'] > now_utc - delta]
eth_trades = [t for t in eth_trades_copy if not delta or t['time'] > now_utc - delta]
btc_lines = build_top_trades_table(f"BTC - {title}", btc_trades)
eth_lines = build_top_trades_table(f"ETH - {title}", eth_trades)
for i in range(len(btc_lines)):
output_lines.append(f"{btc_lines[i]:<45}{separator}{eth_lines[i] if i < len(eth_lines) else ''}")
output_lines.append("")
elif view == "takers":
output_lines.append("--- Top 5 Takers by Volume (Rolling Windows) ---")
for title, delta in time_windows[1:]:
btc_trades = [t for t in btc_trades_copy if t['time'] > now_utc - delta]
eth_trades = [t for t in eth_trades_copy if t['time'] > now_utc - delta]
btc_lines = build_top_takers_table(f"BTC - {title}", btc_trades)
eth_lines = build_top_takers_table(f"ETH - {title}", eth_trades)
for i in range(len(btc_lines)):
output_lines.append(f"{btc_lines[i]:<45}{separator}{eth_lines[i] if i < len(eth_lines) else ''}")
output_lines.append("")
elif view == "active_takers":
output_lines.append("--- Top 5 Active Takers by Trade Count (Rolling Windows) ---")
for title, delta in time_windows[1:]:
btc_trades = [t for t in btc_trades_copy if t['time'] > now_utc - delta]
eth_trades = [t for t in eth_trades_copy if t['time'] > now_utc - delta]
btc_lines = build_top_active_takers_table(f"BTC - {title}", btc_trades)
eth_lines = build_top_active_takers_table(f"ETH - {title}", eth_trades)
header_width = 85
for i in range(len(btc_lines)):
output_lines.append(f"{btc_lines[i]:<{header_width}}{separator}{eth_lines[i] if i < len(eth_lines) else ''}")
output_lines.append("")
if decoded_trade_output:
output_lines.extend(decoded_trade_output)
else:
for _ in range(17): output_lines.append("")
final_output = "\n".join(output_lines) + "\n\x1b[J"
print(final_output, end="")
_lines_printed = len(output_lines)
sys.stdout.flush()
def main():
"""Main function to set up the WebSocket and run the display loop."""
parser = argparse.ArgumentParser(description="Live market data dashboard for Hyperliquid.")
parser.add_argument("--view", default="trades", choices=['trades', 'takers', 'active_takers'],
help="The data view to display: 'trades' (default), 'takers', or 'active_takers'.")
args = parser.parse_args()
coins_to_watch = get_coins_from_strategies()
if not ("BTC" in coins_to_watch and "ETH" in coins_to_watch):
print("This script is configured to display BTC and ETH. Please ensure they are in your strategies.", file=sys.stderr)
return
info = Info(constants.MAINNET_API_URL, skip_ws=False)
for coin in ["BTC", "ETH"]:
trade_subscription = {"type": "trades", "coin": coin}
info.subscribe(trade_subscription, on_message)
print(f"Subscribed to Trades for {coin}")
time.sleep(0.2)
print(f"\nDisplaying live '{args.view}' summary... Press Ctrl+C to stop.")
try:
while True:
update_decoded_trade_display()
display_dashboard(view=args.view)
time.sleep(1)
except KeyboardInterrupt:
print("\nStopping WebSocket listener...")
info.ws_manager.stop()
print("Listener stopped.")
if __name__ == "__main__":
main()