diff --git a/freqtrade/exchange/hyperliquid.py b/freqtrade/exchange/hyperliquid.py index 1e5716acd..6588af0da 100644 --- a/freqtrade/exchange/hyperliquid.py +++ b/freqtrade/exchange/hyperliquid.py @@ -7,7 +7,8 @@ from freqtrade.constants import BuySell from freqtrade.enums import MarginMode, TradingMode from freqtrade.exceptions import ExchangeError, OperationalException from freqtrade.exchange import Exchange -from freqtrade.exchange.exchange_types import FtHas +from freqtrade.exchange.exchange_types import CcxtOrder, FtHas +from freqtrade.util.datetime_helpers import dt_from_ts logger = logging.getLogger(__name__) @@ -155,3 +156,26 @@ class Hyperliquid(Exchange): except ExchangeError: logger.warning(f"Could not update funding fees for {pair}.") return 0.0 + + def fetch_order(self, order_id: str, pair: str, params: dict | None = None) -> CcxtOrder: + order = super().fetch_order(order_id, pair, params) + + if ( + order["average"] is None + and order["status"] in ("canceled", "closed") + and order["filled"] > 0 + ): + # Hyperliquid does not fill the average price in the order response + # Fetch trades to calculate the average price to have the actual price + # the order was executed at + trades = self.get_trades_for_order(order_id, pair, since=dt_from_ts(order["timestamp"])) + + if trades: + total_amount = sum(t["amount"] for t in trades) + order["average"] = ( + sum(t["price"] * t["amount"] for t in trades) / total_amount + if total_amount + else None + ) + + return order diff --git a/tests/exchange/test_hyperliquid.py b/tests/exchange/test_hyperliquid.py index b754685f8..808bef802 100644 --- a/tests/exchange/test_hyperliquid.py +++ b/tests/exchange/test_hyperliquid.py @@ -372,3 +372,46 @@ def test_hyperliquid__lev_prep(default_conf, mocker): assert api_mock.set_margin_mode.call_count == 1 api_mock.set_margin_mode.assert_called_with("isolated", "BTC/USDC:USDC", {"leverage": 19}) + + +def test_hyperliquid_fetch_order(default_conf_usdt, mocker): + default_conf_usdt["dry_run"] = False + + api_mock = MagicMock() + api_mock.fetch_order = MagicMock( + return_value={ + "id": "12345", + "symbol": "ETH/USDC:USDC", + "status": "closed", + "filled": 0.1, + "average": None, + "timestamp": 1630000000, + } + ) + + mocker.patch(f"{EXMS}.exchange_has", return_value=True) + gtfo_mock = mocker.patch( + f"{EXMS}.get_trades_for_order", + return_value=[ + { + "order_id": "12345", + "price": 1000, + "amount": 3, + "filled": 3, + "remaining": 0, + }, + { + "order_id": "12345", + "price": 3000, + "amount": 1, + "filled": 1, + "remaining": 0, + }, + ], + ) + exchange = get_patched_exchange(mocker, default_conf_usdt, api_mock, exchange="hyperliquid") + o = exchange.fetch_order("12345", "ETH/USDC:USDC") + # Uses weighted average + assert o["average"] == 1500 + + assert gtfo_mock.call_count == 1