Merge pull request #12309 from freqtrade/feat/hyperliquid_cross

hyperliquid cross futures support
This commit is contained in:
Matthias
2025-10-02 18:06:47 +02:00
committed by GitHub
4 changed files with 41 additions and 8 deletions

View File

@@ -11,7 +11,7 @@
| [Gate.io](exchanges.md#gateio) | futures | isolated | limit |
| [HTX](exchanges.md#htx) | spot | | limit |
| [Hyperliquid](exchanges.md#hyperliquid) | spot | | ❌ (not supported) |
| [Hyperliquid](exchanges.md#hyperliquid) | futures | isolated | limit |
| [Hyperliquid](exchanges.md#hyperliquid) | futures | isolated, cross | limit |
| [Kraken](exchanges.md#kraken) | spot | | market, limit |
| [OKX](exchanges.md#okx) | spot | | limit |
| [OKX](exchanges.md#okx) | futures | isolated | limit |

View File

@@ -92,6 +92,11 @@ One account is used to share collateral between markets (trading pairs). Margin
Please read the [exchange specific notes](exchanges.md) for exchanges that support this mode and how they differ.
!!! Warning "Increased risk of liquidation"
Cross margin mode increases the risk of full account liquidation, as all trades share the same collateral.
A loss on one trade can affect the liquidation price of other trades.
Also, cross-position influence may not be fully simulated in dry-run or backtesting mode.
## Set leverage to use
Different strategies and risk profiles will require different levels of leverage.

View File

@@ -44,6 +44,7 @@ class Hyperliquid(Exchange):
_supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [
(TradingMode.SPOT, MarginMode.NONE),
(TradingMode.FUTURES, MarginMode.ISOLATED),
(TradingMode.FUTURES, MarginMode.CROSS),
]
@property
@@ -99,7 +100,6 @@ class Hyperliquid(Exchange):
'SOL/USDC:USDC': 43}}
"""
# Defining/renaming variables to match the documentation
isolated_margin = wallet_balance
position_size = amount
price = open_rate
position_value = price * position_size
@@ -117,8 +117,14 @@ class Hyperliquid(Exchange):
# 3. Divide this by 2
maintenance_margin_required = position_value / max_leverage / 2
# Docs: margin_available (isolated) = isolated_margin - maintenance_margin_required
margin_available = isolated_margin - maintenance_margin_required
if self.margin_mode == MarginMode.ISOLATED:
# Docs: margin_available (isolated) = isolated_margin - maintenance_margin_required
margin_available = stake_amount - maintenance_margin_required
elif self.margin_mode == MarginMode.CROSS:
# Docs: margin_available (cross) = account_value - maintenance_margin_required
margin_available = wallet_balance - maintenance_margin_required
else:
raise OperationalException("Unsupported margin mode for liquidation price calculation")
# Docs: The maintenance margin is half of the initial margin at max leverage
# The docs don't explicitly specify maintenance leverage, but this works.

View File

@@ -6,7 +6,8 @@ import pytest
from tests.conftest import EXMS, get_mock_coro, get_patched_exchange
def test_hyperliquid_dry_run_liquidation_price(default_conf, mocker):
@pytest.mark.parametrize("margin_mode", ["isolated", "cross"])
def test_hyperliquid_dry_run_liquidation_price(default_conf, mocker, margin_mode):
# test if liq price calculated by dry_run_liquidation_price() is close to ccxt liq price
# testing different pairs with large/small prices, different leverages, long, short
markets = {
@@ -281,7 +282,7 @@ def test_hyperliquid_dry_run_liquidation_price(default_conf, mocker):
api_mock = MagicMock()
default_conf["trading_mode"] = "futures"
default_conf["margin_mode"] = "isolated"
default_conf["margin_mode"] = margin_mode
default_conf["stake_currency"] = "USDC"
api_mock.load_markets = get_mock_coro()
api_mock.markets = markets
@@ -299,11 +300,32 @@ def test_hyperliquid_dry_run_liquidation_price(default_conf, mocker):
position["contracts"],
position["collateral"],
position["leverage"],
position["collateral"],
[],
# isolated doesn't use wallet-balance
wallet_balance=0.0 if margin_mode == "isolated" else position["collateral"],
open_trades=[],
)
# Assume full position size is the wallet balance
assert pytest.approx(liq_price_returned, rel=0.0001) == liq_price_calculated
if margin_mode == "cross":
# test with larger wallet balance
liq_price_calculated_cross = exchange.dry_run_liquidation_price(
position["symbol"],
position["entryPrice"],
is_short,
position["contracts"],
position["collateral"],
position["leverage"],
wallet_balance=position["collateral"] * 2,
open_trades=[],
)
# Assume full position size is the wallet balance
# This
if position["side"] == "long":
assert liq_price_returned > liq_price_calculated_cross < position["entryPrice"]
else:
assert liq_price_returned < liq_price_calculated_cross > position["entryPrice"]
def test_hyperliquid_get_funding_fees(default_conf, mocker):
now = datetime.now(UTC)