From 71f03ffbc6260110e729f68673337d33d71c6501 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Mar 2026 15:15:46 +0100 Subject: [PATCH] feat: support hyperliquid unified account --- freqtrade/exchange/hyperliquid.py | 48 +++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/hyperliquid.py b/freqtrade/exchange/hyperliquid.py index 4a9e1547b..9b329de93 100644 --- a/freqtrade/exchange/hyperliquid.py +++ b/freqtrade/exchange/hyperliquid.py @@ -5,11 +5,20 @@ from copy import deepcopy from datetime import datetime from typing import Any +import ccxt + from freqtrade.constants import BuySell from freqtrade.enums import MarginMode, TradingMode from freqtrade.enums.runmode import NON_UTIL_MODES -from freqtrade.exceptions import ConfigurationError, ExchangeError, OperationalException +from freqtrade.exceptions import ( + ConfigurationError, + DDosProtection, + ExchangeError, + OperationalException, + TemporaryError, +) from freqtrade.exchange import Exchange +from freqtrade.exchange.common import retrier from freqtrade.exchange.exchange_types import CcxtBalances, CcxtOrder, CcxtPosition, FtHas from freqtrade.util.datetime_helpers import dt_from_ts @@ -22,6 +31,8 @@ class Hyperliquid(Exchange): Contains adjustments needed for Freqtrade to work with this exchange. """ + unified_account = False + _ft_has: FtHas = { "ohlcv_has_history": False, "l2_limit_range": [20], @@ -58,6 +69,36 @@ class Hyperliquid(Exchange): config.update(super()._ccxt_config) return config + @retrier + def additional_exchange_init(self) -> None: + """ + Additional exchange initialization logic. + .api will be available at this point. + Query User account Account Type to determine unified account status + https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#query-a-users-abstraction-state + """ + + try: + if self.trading_mode == TradingMode.FUTURES and not self._config["dry_run"]: + # Determine account status + # Unified accounts must use the spot endpoint for balances + request = { + "type": "userAbstraction", + "user": self._api.walletAddress, + } + response = self._api.publicPostInfo(request) + self.unified_account = response in ('"unifiedAccount"', '"portfolioMargin"') + + except ccxt.DDoSProtection as e: + raise DDosProtection(e) from e + except (ccxt.OperationFailed, ccxt.ExchangeError) as e: + raise TemporaryError( + f"Error in additional_exchange_init due to {e.__class__.__name__}. Message: {e}" + ) from e + + except ccxt.BaseError as e: + raise OperationalException(e) from e + def _get_configured_hip3_dexes(self) -> list[str]: """Get list of configured HIP-3 DEXes.""" return self._config.get("exchange", {}).get("hip3_dexes", []) @@ -122,7 +163,10 @@ class Hyperliquid(Exchange): This override is not absolutely necessary and is only there for correct used / total values which are however not used by Freqtrade in futures mode at the moment. """ - balances = super().get_balances() + params = params or {} + if self.unified_account: + params["type"] = "spot" + balances = super().get_balances(params) dexes = self._get_configured_hip3_dexes() for dex in dexes: try: