HIP3 dexes now fetched by market info of specific pairs
This commit is contained in:
@@ -3,7 +3,6 @@
|
|||||||
import logging
|
import logging
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
from freqtrade.constants import BuySell
|
from freqtrade.constants import BuySell
|
||||||
from freqtrade.enums import MarginMode, TradingMode
|
from freqtrade.enums import MarginMode, TradingMode
|
||||||
@@ -37,9 +36,9 @@ class Hyperliquid(Exchange):
|
|||||||
"stoploss_order_types": {"limit": "limit"},
|
"stoploss_order_types": {"limit": "limit"},
|
||||||
"stoploss_blocks_assets": False,
|
"stoploss_blocks_assets": False,
|
||||||
"stop_price_prop": "stopPrice",
|
"stop_price_prop": "stopPrice",
|
||||||
"funding_fee_timeframe": "1h",
|
|
||||||
"funding_fee_candle_limit": 500,
|
"funding_fee_candle_limit": 500,
|
||||||
"uses_leverage_tiers": False,
|
"uses_leverage_tiers": False,
|
||||||
|
"mark_ohlcv_price": "futures",
|
||||||
}
|
}
|
||||||
|
|
||||||
_supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [
|
_supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [
|
||||||
@@ -57,125 +56,58 @@ class Hyperliquid(Exchange):
|
|||||||
config.update(super()._ccxt_config)
|
config.update(super()._ccxt_config)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
def market_is_tradable(self, market: dict[str, Any]) -> bool:
|
def _get_required_hip3_dexes(self) -> set[str]:
|
||||||
parent_check = super().market_is_tradable(market)
|
"""
|
||||||
hip3_dexes = self._config.get("exchange", {}).get("hip3_dexes", [])
|
Get HIP-3 DEXes that are needed based on tradable pairs.
|
||||||
|
Completely automatic - no configuration needed!
|
||||||
|
"""
|
||||||
|
required_dexes = set()
|
||||||
|
|
||||||
# Allow HIP-3 markets (with ':' in base) only if hip3_dexes configured
|
for market in self.markets.values():
|
||||||
if ":" in market["base"]:
|
if not super().market_is_tradable(market):
|
||||||
return parent_check and bool(hip3_dexes)
|
continue
|
||||||
|
|
||||||
return parent_check
|
market_info = market.get("info", {})
|
||||||
|
if market_info.get("hip3") is True:
|
||||||
|
dex = market_info.get("dex")
|
||||||
|
if dex:
|
||||||
|
required_dexes.add(dex)
|
||||||
|
|
||||||
|
return required_dexes
|
||||||
|
|
||||||
def get_balances(self) -> dict:
|
def get_balances(self) -> dict:
|
||||||
"""
|
"""Fetch balances from default DEX and HIP-3 DEXes needed by tradable pairs."""
|
||||||
Fetch balance including HIP-3 DEX balances if configured.
|
|
||||||
|
|
||||||
For Hyperliquid, this fetches balances from the default DEX and any
|
|
||||||
configured HIP-3 DEXes (e.g., 'xyz' for equities).
|
|
||||||
"""
|
|
||||||
balances = super().get_balances()
|
balances = super().get_balances()
|
||||||
|
|
||||||
# Fetch raw balance info (CCXT normalizes and removes 'info')
|
for dex in self._get_required_hip3_dexes():
|
||||||
try:
|
|
||||||
raw_balance = self._api.fetch_balance()
|
|
||||||
if "info" in raw_balance:
|
|
||||||
balances["info"] = raw_balance["info"]
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"Could not fetch raw balance info: {e}")
|
|
||||||
|
|
||||||
# Fetch HIP-3 DEX balances if configured
|
|
||||||
hip3_dexes = self._config.get("exchange", {}).get("hip3_dexes", [])
|
|
||||||
if hip3_dexes:
|
|
||||||
balances = self._fetch_hip3_balances(hip3_dexes, balances)
|
|
||||||
|
|
||||||
return balances
|
|
||||||
|
|
||||||
def _fetch_hip3_balances(self, hip3_dexes: list[str], base_balance: dict) -> dict:
|
|
||||||
"""Fetch balances from configured HIP-3 DEXes and merge them."""
|
|
||||||
logger.info(f"Fetching balances from {len(hip3_dexes)} HIP-3 DEX(es): {hip3_dexes}")
|
|
||||||
|
|
||||||
for dex in hip3_dexes:
|
|
||||||
try:
|
try:
|
||||||
logger.debug(f"Fetching balance for HIP-3 DEX: {dex}")
|
|
||||||
dex_balance = self._api.fetch_balance({"dex": dex})
|
dex_balance = self._api.fetch_balance({"dex": dex})
|
||||||
|
|
||||||
if not dex_balance or "info" not in dex_balance:
|
for currency, amount_info in dex_balance.items():
|
||||||
logger.error(
|
if currency in ["info", "free", "used", "total", "datetime", "timestamp"]:
|
||||||
f"HIP-3 DEX '{dex}' returned invalid response. Check configuration."
|
continue
|
||||||
)
|
|
||||||
continue
|
|
||||||
|
|
||||||
base_balance = self._merge_hip3_balances(base_balance, dex_balance)
|
if currency not in balances:
|
||||||
|
balances[currency] = amount_info
|
||||||
positions_count = len(dex_balance.get("info", {}).get("assetPositions", []))
|
else:
|
||||||
if positions_count > 0:
|
balances[currency]["free"] += amount_info["free"]
|
||||||
logger.info(f"Merged {positions_count} position(s) from HIP-3 DEX '{dex}'")
|
balances[currency]["used"] += amount_info["used"]
|
||||||
else:
|
balances[currency]["total"] += amount_info["total"]
|
||||||
logger.warning(f"HIP-3 DEX '{dex}' returned no positions")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Could not fetch balance for HIP-3 DEX '{dex}': {e}")
|
logger.error(f"Could not fetch balance for HIP-3 DEX '{dex}': {e}")
|
||||||
|
|
||||||
return base_balance
|
return balances
|
||||||
|
|
||||||
def _merge_hip3_balances(self, base_balance: dict, new_balance: dict) -> dict:
|
|
||||||
"""Merge balances from different Hyperliquid DEXes."""
|
|
||||||
# Merge assetPositions (raw API response)
|
|
||||||
if "info" in new_balance and "assetPositions" in new_balance.get("info", {}):
|
|
||||||
if "info" not in base_balance:
|
|
||||||
base_balance["info"] = {}
|
|
||||||
if "assetPositions" not in base_balance["info"]:
|
|
||||||
base_balance["info"]["assetPositions"] = []
|
|
||||||
|
|
||||||
new_positions = new_balance["info"]["assetPositions"]
|
|
||||||
base_balance["info"]["assetPositions"].extend(new_positions)
|
|
||||||
logger.debug(f"Merged {len(new_positions)} asset position(s) from HIP-3 DEX")
|
|
||||||
|
|
||||||
# Merge normalized CCXT balance structure (skip standard fields)
|
|
||||||
for currency, amount_info in new_balance.items():
|
|
||||||
if currency in ["info", "USDC", "free", "used", "total"]:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if isinstance(amount_info, dict):
|
|
||||||
normalized_currency = currency.upper() if ":" in currency else currency
|
|
||||||
base_balance[normalized_currency] = amount_info
|
|
||||||
logger.debug(f"Added balance for currency: {normalized_currency}")
|
|
||||||
|
|
||||||
return base_balance
|
|
||||||
|
|
||||||
def fetch_positions(self, pair: str | None = None) -> list[CcxtPosition]:
|
def fetch_positions(self, pair: str | None = None) -> list[CcxtPosition]:
|
||||||
"""
|
"""Fetch positions from default DEX and HIP-3 DEXes needed by tradable pairs."""
|
||||||
Fetch positions including HIP-3 positions.
|
positions = super().fetch_positions(pair)
|
||||||
|
|
||||||
Hyperliquid supports fetching positions from different DEXes using the
|
for dex in self._get_required_hip3_dexes():
|
||||||
'dex' parameter. This method fetches positions from the default DEX and
|
try:
|
||||||
all configured HIP-3 DEXes.
|
positions.extend(self._api.fetch_positions(params={"dex": dex}))
|
||||||
"""
|
except Exception as e:
|
||||||
# Fetch standard positions from default DEX
|
logger.error(f"Could not fetch positions from HIP-3 DEX '{dex}': {e}")
|
||||||
try:
|
|
||||||
if pair:
|
|
||||||
positions = self._api.fetch_positions([pair])
|
|
||||||
else:
|
|
||||||
positions = self._api.fetch_positions()
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"Could not fetch standard positions: {e}")
|
|
||||||
positions = []
|
|
||||||
|
|
||||||
# Fetch HIP-3 positions if configured
|
|
||||||
hip3_dexes = self._config.get("exchange", {}).get("hip3_dexes", [])
|
|
||||||
if hip3_dexes:
|
|
||||||
for dex in hip3_dexes:
|
|
||||||
try:
|
|
||||||
logger.debug(f"Fetching positions from HIP-3 DEX: {dex}")
|
|
||||||
hip3_positions = self._api.fetch_positions(params={"dex": dex})
|
|
||||||
|
|
||||||
if hip3_positions:
|
|
||||||
positions.extend(hip3_positions)
|
|
||||||
logger.debug(f"Added {len(hip3_positions)} position(s) from DEX '{dex}'")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Could not fetch positions from HIP-3 DEX '{dex}': {e}")
|
|
||||||
|
|
||||||
return positions
|
return positions
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user