From ade9b708a21640ce4f94c794a3ef24ae8c403424 Mon Sep 17 00:00:00 2001 From: DiTus Date: Thu, 30 Jul 2026 21:15:48 +0200 Subject: [PATCH] Add account data fetching and display balances in dashboard --- __pycache__/logging_utils.cpython-313.pyc | Bin 3013 -> 0 bytes __pycache__/trade_log.cpython-313.pyc | Bin 2574 -> 0 bytes fetch_history.py | 101 ++++++++++++++++++++++ main_app.py | 47 +++++++++- position_monitor.py | 22 ++++- 5 files changed, 163 insertions(+), 7 deletions(-) delete mode 100644 __pycache__/logging_utils.cpython-313.pyc delete mode 100644 __pycache__/trade_log.cpython-313.pyc create mode 100644 fetch_history.py diff --git a/__pycache__/logging_utils.cpython-313.pyc b/__pycache__/logging_utils.cpython-313.pyc deleted file mode 100644 index 6a8df43fe1c42c95f44b1c05937e45ef178f2d8a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3013 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 now_ms: + print(f"{coin} ({tf}): Already up to date") + continue + + print(f"{coin} ({tf}): Fetching historical candles from {datetime.fromtimestamp(start_ts/1000, tz=timezone.utc)} to {datetime.fromtimestamp(now_ms/1000, tz=timezone.utc)}...") + candles = fetch_historical_candles(coin, start_ts, now_ms, interval=tf) + print(f"{coin} ({tf}): Fetched {len(candles)} candles") + write_candles_to_db(coin, candles, interval=tf) + print(f"{coin} ({tf}): Written to database") + +print("Done!") diff --git a/main_app.py b/main_app.py index 69e5243..aeb3ee8 100644 --- a/main_app.py +++ b/main_app.py @@ -10,17 +10,18 @@ import sqlite3 import pandas as pd from datetime import datetime import importlib +from dotenv import load_dotenv +load_dotenv() # --- REMOVED: import signal --- # --- REMOVED: from queue import Empty --- from logging_utils import setup_logging -# --- Using the new high-performance WebSocket utility for live prices --- from live_market_utils import start_live_feed -# --- Import the base class for type hinting (optional but good practice) --- from strategies.base_strategy import BaseStrategy -# --- Rich dashboard renderer --- from dashboard import DashboardRenderer from rich.live import Live +from hyperliquid.info import Info +from hyperliquid.utils import constants # --- Configuration --- WATCHED_COINS = ["BTC", "ETH", "SOL", "BNB", "HYPE", "SUI", "xyz:BRENTOIL", "xyz:CL", "xyz:GOLD", "xyz:SILVER", "mkts:USTECH", "xyz:XYZ100"] @@ -384,10 +385,17 @@ class MainApp: self.strategy_configs = strategy_configs self.strategy_statuses = {} self.indicators_status = {} + self.account_data = None + self.wallet_address = os.environ.get("MAIN_WALLET_ADDRESS") + if self.wallet_address: + self.info_client = Info(constants.MAINNET_API_URL, skip_ws=True) + else: + self.info_client = None self.renderer = DashboardRenderer(table_visibility={ "market": True, "strategies": False, "indicators": True, + "balances": True, }) def read_prices(self): @@ -428,6 +436,35 @@ class MainApp: else: self.indicators_status = {} + def read_account_data(self): + """Fetches account balances and positions from Hyperliquid API.""" + if not self.wallet_address or not self.info_client: + self.account_data = None + return + try: + perp_state = self.info_client.user_state(self.wallet_address) + spot_state = self.info_client.spot_user_state(self.wallet_address) + + margin_summary = perp_state.get('marginSummary', {}) + account_value = float(margin_summary.get('accountValue', 0)) + margin_used = float(margin_summary.get('totalMarginUsed', 0)) + utilization = (margin_used / account_value) * 100 if account_value > 0 else 0 + + spot_balances = spot_state.get('balances', []) + positions = perp_state.get('assetPositions', []) + + self.account_data = { + 'account_value': account_value, + 'margin_used': margin_used, + 'utilization': utilization, + 'spot_balances': spot_balances, + 'positions': positions, + } + + except Exception as e: + logging.error(f"Could not fetch account data: {e}") + self.account_data = None + def check_process_status(self): """Checks if the background processes are still running.""" for name, process in self.background_processes.items(): @@ -445,7 +482,8 @@ class MainApp: COIN_DISPLAY_NAMES, self.strategy_statuses, self.strategy_configs, - self.indicators_status + self.indicators_status, + self.account_data ) def run(self): @@ -455,6 +493,7 @@ class MainApp: self.read_prices() self.read_strategy_statuses() self.read_indicators_status() + self.read_account_data() live.update(self.display_dashboard()) time.sleep(0.5) diff --git a/position_monitor.py b/position_monitor.py index e20ec5d..b017dce 100644 --- a/position_monitor.py +++ b/position_monitor.py @@ -77,14 +77,13 @@ class PositionMonitor: output_lines.append("\n--- Perpetuals Account Summary ---") output_lines.append(f" Account Value: ${account_value:,.2f} | Margin Used: ${margin_used:,.2f} | Utilization: {utilization:.2f}%") - # --- 2. Spot Balances Summary --- + # --- 2. Spot Balances Table --- output_lines.append("\n--- Spot Balances ---") spot_balances = spot_state.get('balances', []) if not spot_balances: output_lines.append(" No spot balances found.") else: - balances_str = ", ".join([f"{b.get('coin')}: {float(b.get('total', 0)):,.4f}" for b in spot_balances if float(b.get('total', 0)) > 0]) - output_lines.append(f" {balances_str}") + self.build_spot_balances_table(spot_balances, output_lines) # --- 3. Open Positions Table --- output_lines.append("\n--- Open Perpetual Positions ---") @@ -106,6 +105,23 @@ class PositionMonitor: self._lines_printed = len(output_lines) sys.stdout.flush() + def build_spot_balances_table(self, spot_balances: list, output_lines: list): + """Builds the text for the spot balances table.""" + header = f"| {'Coin':<10} | {'Total':>18} |" + output_lines.append(header) + output_lines.append("-" * len(header)) + + for balance in spot_balances: + coin = balance.get('coin', 'Unknown') + total = float(balance.get('total', 0)) + + coin_str = f"{coin:<10}" + total_str = f"{total:>18,.4f}" + + output_lines.append(f"| {coin_str} | {total_str} |") + + output_lines.append("-" * len(header)) + def build_positions_table(self, positions: list, coin_to_strategy_map: dict, output_lines: list): """Builds the text for the positions summary table.""" header = f"| {'Strategy':<25} | {'Coin':<6} | {'Side':<5} | {'Size':>15} | {'Entry Price':>12} | {'Mark Price':>12} | {'PNL':>15} | {'Leverage':>10} |"