Add account data fetching and display balances in dashboard

This commit is contained in:
DiTus
2026-07-30 21:15:48 +02:00
parent 76f58386dc
commit ade9b708a2
5 changed files with 163 additions and 7 deletions

101
fetch_history.py Normal file
View File

@ -0,0 +1,101 @@
import requests
import json
import sqlite3
import time
from datetime import datetime, timezone
DB_PATH = "_data/market_data.db"
URL = "https://api.hyperliquid.xyz/info"
def fetch_historical_candles(coin, start_ms, end_ms, interval="1m"):
"""Fetch historical candles using the raw HTTP API."""
candles = []
current_start = start_ms
while current_start < end_ms:
payload = {
"type": "candleSnapshot",
"req": {
"coin": coin,
"interval": interval,
"startTime": current_start,
"endTime": end_ms
}
}
resp = requests.post(URL, json=payload)
batch = resp.json()
if not batch:
break
for candle in batch:
candle['coin'] = coin
candles.append(candle)
last_ts = batch[-1]['t']
if last_ts < current_start:
break
current_start = last_ts + 1
time.sleep(0.5)
return candles
def write_candles_to_db(coin, candles, interval="1m"):
"""Write candles to the database."""
table_name = coin + "_" + interval
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
# Ensure table exists
cursor.execute(f'''
CREATE TABLE IF NOT EXISTS "{table_name}" (
datetime_utc TEXT,
timestamp_ms INTEGER PRIMARY KEY,
open REAL,
high REAL,
low REAL,
close REAL,
volume REAL,
number_of_trades INTEGER
)
''')
for candle in candles:
record = (
datetime.fromtimestamp(candle['t'] / 1000, tz=timezone.utc).strftime('%Y-%m-%d %H:%M:%S'),
candle['t'],
candle.get('o'), candle.get('h'), candle.get('l'), candle.get('c'),
candle.get('v'), candle.get('n')
)
cursor.execute(f'''
INSERT OR REPLACE INTO "{table_name}" (datetime_utc, timestamp_ms, open, high, low, close, volume, number_of_trades)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
''', record)
conn.commit()
conn.close()
def get_last_timestamp(coin):
"""Get the most recent timestamp from the database."""
table_name = coin + "_1m"
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
try:
cursor.execute(f'SELECT MAX(timestamp_ms) FROM "{table_name}"')
result = cursor.fetchone()
return int(result[0]) if result and result[0] is not None else None
except:
return None
finally:
conn.close()
coins = ["mkts:USTECH", "xyz:XYZ100"]
now_ms = int(time.time() * 1000)
seven_days_ms = 7 * 24 * 60 * 60 * 1000
for coin in coins:
for tf in ["1m", "1d"]:
start_ts = now_ms - seven_days_ms
if start_ts >= 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!")

View File

@ -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)

View File

@ -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} |"