Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
670999a
compatible with HIP3
igi01 Dec 3, 2025
7a23da4
only use HIP3_dexes with hyperliquid validation
igi01 Dec 3, 2025
c892b07
compatible with HIP3
igi01 Dec 3, 2025
3ebc7bf
no parsing direct position call
igi01 Dec 3, 2025
922d589
newest ccxt
igi01 Dec 4, 2025
2045fa6
Delete freqtrade/configuration/config_validation.py
igi01 Dec 15, 2025
789f4aa
Delete requirements.txt
igi01 Dec 15, 2025
f32d25d
accidentaly deleted file and not the diff
igi01 Dec 17, 2025
245ca4b
accidentaly deleted file and not the diff
igi01 Dec 17, 2025
301aff1
HIP3 dexes now fetched by market info of specific pairs
igi01 Dec 17, 2025
b2d5343
accidentaly deleted file and not the diff
igi01 Dec 18, 2025
29b7cbb
HIP3 dexes now fetched by market info of specific pairs
igi01 Dec 18, 2025
9023ca4
Merge branch 'freqtrade:develop' into feature/hyperliquid-hip3-support
igi01 Dec 21, 2025
bcf6c24
Add config validation + market tradable checks
igi01 Dec 21, 2025
ce8732b
modified tests for hip3 assets + add new tests for hip3 assets
igi01 Dec 21, 2025
bcd033b
remove HIP3 dex quantity restrictions
igi01 Dec 21, 2025
45b9c8c
chore: use parent methods for proper error handling
xmatthias Dec 27, 2025
dc4adcc
feat: don't limit pairs in "list-pairs" mode
xmatthias Dec 27, 2025
e71329c
fix: DEX's are isolated margin only
xmatthias Dec 27, 2025
61eb330
docs: fix documentation formatting and wording
xmatthias Dec 27, 2025
2708343
fix: only validate HIP3 in futures mode
xmatthias Dec 27, 2025
1dde1ad
test: fix and improve hip3 config validation test
xmatthias Dec 27, 2025
00de807
test: fix hyperliquid positions test
xmatthias Dec 27, 2025
414fe81
test: reduce test position data
xmatthias Dec 27, 2025
b26c37a
test: test online to ensure HIP3 pairs are in the markets object
xmatthias Dec 27, 2025
e77e901
fix: don't ignore regular tradability rules
xmatthias Dec 27, 2025
9b99c5b
chore: Add explanation for get_balances
xmatthias Dec 27, 2025
b394705
fix: improve check tightness in hyperliquid hip3 config
xmatthias Dec 27, 2025
b8cf38c
test: fix a couple hyperliquid tests
xmatthias Dec 27, 2025
6e9b60c
fix: use ConfigurationError instead of OperationalException
xmatthias Dec 28, 2025
de94170
test: update and improve validation tests
xmatthias Dec 28, 2025
511b817
test: test exception case for one dex call
xmatthias Dec 28, 2025
0dad2b3
Merge branch 'develop' into feature/hyperliquid-hip3-support
xmatthias Dec 30, 2025
75a3651
test: ensure hyperliquid works without dex configured
xmatthias Dec 30, 2025
0a94a6d
test: refactor hyperliquid fake markets into a fixture
xmatthias Dec 30, 2025
2a1b3ae
test: improve test to disable different stake currency markets
xmatthias Dec 30, 2025
0f8c6d4
fix: only matching quote currencies should be tradable
xmatthias Dec 30, 2025
8e91fea
Merge pull request #12584 from igi01/feature/hyperliquid-hip3-support
xmatthias Dec 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions docs/exchanges.md
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,36 @@ Your balance and trades will now be used from your vault / subaccount - and no l

The Hyperliquid API does not provide historic data beyond the single call to fetch current data, so downloading data is not possible, as the downloaded data would not constitute proper historic data.

### HIP-3 DEXes

Hyperliquid supports HIP-3 decentralized exchanges (DEXes), which are independent exchanges built on top of the Hyperliquid infrastructure.
These DEXes operate similarly to the main Hyperliquid exchange but are community-created and managed.

To trade on HIP-3 DEXes with Freqtrade, you need to add them to your configuration using the `hip3_dexes` parameter:

```json
"exchange": {
"name": "hyperliquid",
"walletAddress": "your_master_wallet_address",
"privateKey": "your_api_private_key",
"hip3_dexes": ["dex_name_1", "dex_name_2"]
}
```

Replace `"dex_name_1"` and `"dex_name_2"` with the actual names of the HIP-3 DEXes you want to trade on (e.g. `vntl` and `xyz`).

!!! Warning "Performance and Rate Limit Impact"
Each HIP-3 DEX you add significantly impacts bot performance and rate limits.

* **Additional API Calls**: For each HIP-3 DEX configured, Freqtrade needs to make additional API calls.
* **Rate Limit Pressure**: Additional API calls contribute to Hyperliquid's strict rate limits. With multiple DEXes, you may hit rate limits faster, or rather, slow down bot operations due to enforced delays.

Please only add HIP-3 DEXes that you actively trade on. Monitor your logs for rate limit warnings or signs of slowed operations, and adjust your configuration accordingly.
Different HIP-3 DEXes may also use different quote currencies - so make sure to only add DEXes that are compatible with your stake currency to avoid unnecessary delays.

!!! Note
HIP-3 DEXes share the same wallet and free amount of collateral as your main Hyperliquid account. Trades on different DEXes will affect your overall account balance and margin.

## Bitvavo

If your account is required to use an operatorId, you can set it in the configuration file as follows:
Expand Down
12 changes: 7 additions & 5 deletions freqtrade/exchange/exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -1827,16 +1827,16 @@ def cancel_stoploss_order_with_result(
return order

@retrier
def get_balances(self) -> CcxtBalances:
def get_balances(self, params: dict | None = None) -> CcxtBalances:
try:
balances = self._api.fetch_balance()
balances = self._api.fetch_balance(params or {})
# Remove additional info from ccxt results
balances.pop("info", None)
balances.pop("free", None)
balances.pop("total", None)
balances.pop("used", None)

self._log_exchange_response("fetch_balance", balances)
self._log_exchange_response("fetch_balance", balances, add_info=params)
return balances
except ccxt.DDoSProtection as e:
raise DDosProtection(e) from e
Expand All @@ -1848,7 +1848,9 @@ def get_balances(self) -> CcxtBalances:
raise OperationalException(e) from e

@retrier
def fetch_positions(self, pair: str | None = None) -> list[CcxtPosition]:
def fetch_positions(
self, pair: str | None = None, params: dict | None = None
) -> list[CcxtPosition]:
"""
Fetch positions from the exchange.
If no pair is given, all positions are returned.
Expand All @@ -1860,7 +1862,7 @@ def fetch_positions(self, pair: str | None = None) -> list[CcxtPosition]:
symbols = None
if pair:
symbols = [pair]
positions: list[CcxtPosition] = self._api.fetch_positions(symbols)
positions: list[CcxtPosition] = self._api.fetch_positions(symbols, params=params or {})
self._log_exchange_response("fetch_positions", positions)
return positions
except ccxt.DDoSProtection as e:
Expand Down
107 changes: 102 additions & 5 deletions freqtrade/exchange/hyperliquid.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@

from freqtrade.constants import BuySell
from freqtrade.enums import MarginMode, TradingMode
from freqtrade.exceptions import ExchangeError, OperationalException
from freqtrade.enums.runmode import NON_UTIL_MODES
from freqtrade.exceptions import ConfigurationError, ExchangeError, OperationalException
from freqtrade.exchange import Exchange
from freqtrade.exchange.exchange_types import CcxtOrder, FtHas
from freqtrade.exchange.exchange_types import CcxtBalances, CcxtOrder, CcxtPosition, FtHas
from freqtrade.util.datetime_helpers import dt_from_ts


Expand Down Expand Up @@ -57,12 +58,108 @@ def _ccxt_config(self) -> dict:
config.update(super()._ccxt_config)
return config

def _get_configured_hip3_dexes(self) -> list[str]:
"""Get list of configured HIP-3 DEXes."""
return self._config.get("exchange", {}).get("hip3_dexes", [])

def validate_config(self, config: dict) -> None:
"""Validate HIP-3 configuration at bot startup."""
super().validate_config(config)
configured = self._get_configured_hip3_dexes()
if not configured or not self.markets:
return
if self.trading_mode != TradingMode.FUTURES:
if configured:
raise ConfigurationError(
"HIP-3 DEXes are only supported in FUTURES trading mode. "
"Please update your configuration!"
)
return
if configured and self.margin_mode != MarginMode.ISOLATED:
raise ConfigurationError(
"HIP-3 DEXes require 'isolated' margin mode. "
f"Current margin mode: '{self.margin_mode.value}'. "
"Please update your configuration!"
)

available = {
m.get("info", {}).get("dex")
for m in self.get_markets(
quote_currencies=[self._config["stake_currency"]],
tradable_only=True,
active_only=True,
).values()
if m.get("info", {}).get("hip3")
}
available.discard(None)

invalid = set(configured) - available
if invalid:
raise ConfigurationError(
f"Invalid HIP-3 DEXes configured: {sorted(invalid)}. "
f"Available DEXes matching your stake currency ({self._config['stake_currency']}): "
f"{sorted(available)}. "
f"Check your 'hip3_dexes' configuration!"
)

def market_is_tradable(self, market: dict[str, Any]) -> bool:
"""Check if market is tradable, including HIP-3 markets."""
parent_check = super().market_is_tradable(market)

# Exclude hip3 markets for now - which have the format XYZ:GOOGL/USDT:USDT -
# and XYZ:GOOGL as base
return parent_check and ":" not in market["base"]
market_info = market.get("info", {})
if market_info.get("hip3") and self._config["runmode"] in NON_UTIL_MODES:
configured = self._get_configured_hip3_dexes()
if not configured:
return False

market_dex = market_info.get("dex")
return parent_check and market_dex in configured

return parent_check

def get_balances(self, params: dict | None = None) -> CcxtBalances:
"""Fetch balances from default DEX and HIP-3 DEXes needed by tradable pairs.
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()
dexes = self._get_configured_hip3_dexes()
for dex in dexes:
try:
dex_balance = super().get_balances(params={"dex": dex})

for currency, amount_info in dex_balance.items():
if currency in ["info", "free", "used", "total", "datetime", "timestamp"]:
continue

if currency not in balances:
balances[currency] = amount_info
else:
balances[currency]["free"] += amount_info["free"]
balances[currency]["used"] += amount_info["used"]
balances[currency]["total"] += amount_info["total"]

except Exception as e:
logger.error(f"Could not fetch balance for HIP-3 DEX '{dex}': {e}")

if dexes:
self._log_exchange_response("fetch_balance", balances, add_info="combined")
return balances

def fetch_positions(
self, pair: str | None = None, params: dict | None = None
) -> list[CcxtPosition]:
"""Fetch positions from default DEX and HIP-3 DEXes needed by tradable pairs."""
positions = super().fetch_positions(pair)
dexes = self._get_configured_hip3_dexes()
for dex in dexes:
try:
positions.extend(super().fetch_positions(pair, params={"dex": dex}))
except Exception as e:
logger.error(f"Could not fetch positions from HIP-3 DEX '{dex}': {e}")
if dexes:
self._log_exchange_response("fetch_positions", positions, add_info="combined")
return positions

def get_max_leverage(self, pair: str, stake_amount: float | None) -> float:
# There are no leverage tiers
Expand Down
2 changes: 1 addition & 1 deletion freqtrade/exchange/kraken.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def consolidate_balances(self, balances: CcxtBalances) -> CcxtBalances:
return consolidated

@retrier
def get_balances(self) -> CcxtBalances:
def get_balances(self, params: dict | None = None) -> CcxtBalances:
if self._config["dry_run"]:
return {}

Expand Down
Loading
Loading