exchange: fallback to canceled orders for krakenfutures fetch_order; add tests

This commit is contained in:
matstedt
2026-01-19 17:08:05 +01:00
committed by Matthias
parent f2780c7c68
commit 1345cfdbee
2 changed files with 48 additions and 9 deletions
+20 -6
View File
@@ -6,7 +6,7 @@ import logging
from typing import Any from typing import Any
from freqtrade.enums import MarginMode, PriceType, TradingMode from freqtrade.enums import MarginMode, PriceType, TradingMode
from freqtrade.exceptions import ExchangeError, RetryableOrderError from freqtrade.exceptions import ExchangeError, RetryableOrderError, TemporaryError
from freqtrade.exchange.exchange import Exchange from freqtrade.exchange.exchange import Exchange
from freqtrade.exchange.exchange_types import FtHas from freqtrade.exchange.exchange_types import FtHas
@@ -169,19 +169,33 @@ class Krakenfutures(Exchange):
""" """
Kraken Futures fetchOrder is backed by /orders/status which only returns Kraken Futures fetchOrder is backed by /orders/status which only returns
open orders or orders closed within the last 5 seconds. open orders or orders closed within the last 5 seconds.
Fall back to fetchClosedOrders for older orders. Fall back to closed/canceled order endpoints for older orders.
""" """
params = params or {} params = params or {}
try: try:
return super().fetch_order(order_id, pair, params=params) return super().fetch_order(order_id, pair, params=params)
except RetryableOrderError as err: except (RetryableOrderError, TemporaryError) as err:
if not self.exchange_has("fetchClosedOrders"): order = self._fetch_order_from_closed_or_canceled(order_id, pair, params)
raise if order is not None:
return order
raise err
def _fetch_order_from_closed_or_canceled(
self, order_id: str, pair: str, params: dict[str, Any]
) -> dict[str, Any] | None:
if self.exchange_has("fetchClosedOrders"):
orders = self._api.fetch_closed_orders(pair, params=params) orders = self._api.fetch_closed_orders(pair, params=params)
for order in orders or []: for order in orders or []:
if str(order.get("id")) == str(order_id): if str(order.get("id")) == str(order_id):
return self._order_contracts_to_amount(order) return self._order_contracts_to_amount(order)
raise err
if self.exchange_has("fetchCanceledOrders"):
orders = self._api.fetch_canceled_orders(pair, params=params)
for order in orders or []:
if str(order.get("id")) == str(order_id):
return self._order_contracts_to_amount(order)
return None
def get_funding_fees(self, pair: str, amount: float, is_short: bool, open_date) -> float: def get_funding_fees(self, pair: str, amount: float, is_short: bool, open_date) -> float:
"""CCXT currently does not support Kraken Futures fetchFundingHistory.""" """CCXT currently does not support Kraken Futures fetchFundingHistory."""
+28 -3
View File
@@ -6,7 +6,7 @@ from copy import deepcopy
from unittest.mock import MagicMock from unittest.mock import MagicMock
from freqtrade.enums import CandleType, MarginMode, TradingMode from freqtrade.enums import CandleType, MarginMode, TradingMode
from freqtrade.exceptions import RetryableOrderError from freqtrade.exceptions import RetryableOrderError, TemporaryError
from freqtrade.exchange.exchange import Exchange from freqtrade.exchange.exchange import Exchange
from freqtrade.exchange.krakenfutures import Krakenfutures from freqtrade.exchange.krakenfutures import Krakenfutures
from tests.conftest import EXMS, get_patched_exchange from tests.conftest import EXMS, get_patched_exchange
@@ -35,10 +35,12 @@ def test_krakenfutures_fetch_order_falls_back_to_closed_orders(mocker, default_c
"""Fallback to fetch_closed_orders when fetch_order can't find the order.""" """Fallback to fetch_closed_orders when fetch_order can't find the order."""
ex = get_patched_exchange(mocker, default_conf, exchange="krakenfutures") ex = get_patched_exchange(mocker, default_conf, exchange="krakenfutures")
mocker.patch.object(Exchange, "fetch_order", side_effect=RetryableOrderError("not found"))
mocker.patch.object( mocker.patch.object(
Exchange, "fetch_order", side_effect=RetryableOrderError("not found") ex,
"exchange_has",
side_effect=lambda endpoint: endpoint == "fetchClosedOrders",
) )
mocker.patch.object(ex, "exchange_has", return_value=True)
mocker.patch.object( mocker.patch.object(
ex._api, ex._api,
"fetch_closed_orders", "fetch_closed_orders",
@@ -50,6 +52,29 @@ def test_krakenfutures_fetch_order_falls_back_to_closed_orders(mocker, default_c
assert res["id"] == "abc" assert res["id"] == "abc"
def test_krakenfutures_fetch_order_falls_back_to_canceled_orders(mocker, default_conf):
"""Fallback to fetch_canceled_orders when closed orders don't contain the order."""
ex = get_patched_exchange(mocker, default_conf, exchange="krakenfutures")
mocker.patch.object(
Exchange, "fetch_order", side_effect=TemporaryError("UUID string too large")
)
mocker.patch.object(
ex,
"exchange_has",
side_effect=lambda endpoint: endpoint == "fetchCanceledOrders",
)
mocker.patch.object(
ex._api,
"fetch_canceled_orders",
return_value=[{"id": "def", "symbol": "BTC/USD:USD", "status": "canceled"}],
create=True,
)
res = ex.fetch_order("def", "BTC/USD:USD")
assert res["id"] == "def"
def test_krakenfutures_create_stoploss_uses_trigger_price_type(mocker, default_conf): def test_krakenfutures_create_stoploss_uses_trigger_price_type(mocker, default_conf):
"""Test create_stoploss uses triggerPrice, triggerSignal, and reduceOnly.""" """Test create_stoploss uses triggerPrice, triggerSignal, and reduceOnly."""
api_mock = MagicMock() api_mock = MagicMock()