Skip to content

Commit 5542feb

Browse files
authored
Merge pull request freqtrade#12309 from freqtrade/feat/hyperliquid_cross
hyperliquid cross futures support
2 parents 4eba2c6 + c7aaa77 commit 5542feb

File tree

4 files changed

+41
-8
lines changed

4 files changed

+41
-8
lines changed

docs/includes/exchange-features.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
| [Gate.io](exchanges.md#gateio) | futures | isolated | limit |
1212
| [HTX](exchanges.md#htx) | spot | | limit |
1313
| [Hyperliquid](exchanges.md#hyperliquid) | spot | | ❌ (not supported) |
14-
| [Hyperliquid](exchanges.md#hyperliquid) | futures | isolated | limit |
14+
| [Hyperliquid](exchanges.md#hyperliquid) | futures | isolated, cross | limit |
1515
| [Kraken](exchanges.md#kraken) | spot | | market, limit |
1616
| [OKX](exchanges.md#okx) | spot | | limit |
1717
| [OKX](exchanges.md#okx) | futures | isolated | limit |

docs/leverage.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ One account is used to share collateral between markets (trading pairs). Margin
9292

9393
Please read the [exchange specific notes](exchanges.md) for exchanges that support this mode and how they differ.
9494

95+
!!! Warning "Increased risk of liquidation"
96+
Cross margin mode increases the risk of full account liquidation, as all trades share the same collateral.
97+
A loss on one trade can affect the liquidation price of other trades.
98+
Also, cross-position influence may not be fully simulated in dry-run or backtesting mode.
99+
95100
## Set leverage to use
96101

97102
Different strategies and risk profiles will require different levels of leverage.

freqtrade/exchange/hyperliquid.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class Hyperliquid(Exchange):
4444
_supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [
4545
(TradingMode.SPOT, MarginMode.NONE),
4646
(TradingMode.FUTURES, MarginMode.ISOLATED),
47+
(TradingMode.FUTURES, MarginMode.CROSS),
4748
]
4849

4950
@property
@@ -99,7 +100,6 @@ def dry_run_liquidation_price(
99100
'SOL/USDC:USDC': 43}}
100101
"""
101102
# Defining/renaming variables to match the documentation
102-
isolated_margin = wallet_balance
103103
position_size = amount
104104
price = open_rate
105105
position_value = price * position_size
@@ -117,8 +117,14 @@ def dry_run_liquidation_price(
117117
# 3. Divide this by 2
118118
maintenance_margin_required = position_value / max_leverage / 2
119119

120-
# Docs: margin_available (isolated) = isolated_margin - maintenance_margin_required
121-
margin_available = isolated_margin - maintenance_margin_required
120+
if self.margin_mode == MarginMode.ISOLATED:
121+
# Docs: margin_available (isolated) = isolated_margin - maintenance_margin_required
122+
margin_available = stake_amount - maintenance_margin_required
123+
elif self.margin_mode == MarginMode.CROSS:
124+
# Docs: margin_available (cross) = account_value - maintenance_margin_required
125+
margin_available = wallet_balance - maintenance_margin_required
126+
else:
127+
raise OperationalException("Unsupported margin mode for liquidation price calculation")
122128

123129
# Docs: The maintenance margin is half of the initial margin at max leverage
124130
# The docs don't explicitly specify maintenance leverage, but this works.

tests/exchange/test_hyperliquid.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
from tests.conftest import EXMS, get_mock_coro, get_patched_exchange
77

88

9-
def test_hyperliquid_dry_run_liquidation_price(default_conf, mocker):
9+
@pytest.mark.parametrize("margin_mode", ["isolated", "cross"])
10+
def test_hyperliquid_dry_run_liquidation_price(default_conf, mocker, margin_mode):
1011
# test if liq price calculated by dry_run_liquidation_price() is close to ccxt liq price
1112
# testing different pairs with large/small prices, different leverages, long, short
1213
markets = {
@@ -281,7 +282,7 @@ def test_hyperliquid_dry_run_liquidation_price(default_conf, mocker):
281282

282283
api_mock = MagicMock()
283284
default_conf["trading_mode"] = "futures"
284-
default_conf["margin_mode"] = "isolated"
285+
default_conf["margin_mode"] = margin_mode
285286
default_conf["stake_currency"] = "USDC"
286287
api_mock.load_markets = get_mock_coro()
287288
api_mock.markets = markets
@@ -299,11 +300,32 @@ def test_hyperliquid_dry_run_liquidation_price(default_conf, mocker):
299300
position["contracts"],
300301
position["collateral"],
301302
position["leverage"],
302-
position["collateral"],
303-
[],
303+
# isolated doesn't use wallet-balance
304+
wallet_balance=0.0 if margin_mode == "isolated" else position["collateral"],
305+
open_trades=[],
304306
)
307+
# Assume full position size is the wallet balance
305308
assert pytest.approx(liq_price_returned, rel=0.0001) == liq_price_calculated
306309

310+
if margin_mode == "cross":
311+
# test with larger wallet balance
312+
liq_price_calculated_cross = exchange.dry_run_liquidation_price(
313+
position["symbol"],
314+
position["entryPrice"],
315+
is_short,
316+
position["contracts"],
317+
position["collateral"],
318+
position["leverage"],
319+
wallet_balance=position["collateral"] * 2,
320+
open_trades=[],
321+
)
322+
# Assume full position size is the wallet balance
323+
# This
324+
if position["side"] == "long":
325+
assert liq_price_returned > liq_price_calculated_cross < position["entryPrice"]
326+
else:
327+
assert liq_price_returned < liq_price_calculated_cross > position["entryPrice"]
328+
307329

308330
def test_hyperliquid_get_funding_fees(default_conf, mocker):
309331
now = datetime.now(UTC)

0 commit comments

Comments
 (0)