From 04ec24e73798dddb77c0cd12ecb2b7f9659ee6b9 Mon Sep 17 00:00:00 2001 From: matstedt Date: Tue, 13 Jan 2026 20:04:08 +0100 Subject: [PATCH] krakenfutures: move ohlcv cap to _ft_has and support futures-only online tests --- freqtrade/exchange/krakenfutures.py | 15 +------ tests/exchange/test_krakenfutures.py | 4 +- tests/exchange_online/conftest.py | 53 ++++++++++++++++------- tests/exchange_online/test_ccxt_compat.py | 7 ++- 4 files changed, 47 insertions(+), 32 deletions(-) diff --git a/freqtrade/exchange/krakenfutures.py b/freqtrade/exchange/krakenfutures.py index 2eba10201..d3580598e 100644 --- a/freqtrade/exchange/krakenfutures.py +++ b/freqtrade/exchange/krakenfutures.py @@ -53,6 +53,7 @@ class KrakenFutures(Exchange): PriceType.MARK: "mark", PriceType.INDEX: "index", }, + "ohlcv_candle_limit": 2000, # override ccxt has-gaps "exchange_has_overrides": { "fetchOrder": True, @@ -593,19 +594,5 @@ class KrakenFutures(Exchange): return v.strip() return "mark" - def ohlcv_candle_limit( - self, - timeframe: str, - candle_type: CandleType, - since_ms: int | None = None, - ) -> int: - limit = super().ohlcv_candle_limit( - timeframe, - candle_type=candle_type, - since_ms=since_ms, - ) - return min(limit, 2000) - - class Krakenfutures(KrakenFutures): pass diff --git a/tests/exchange/test_krakenfutures.py b/tests/exchange/test_krakenfutures.py index 0c6a21a6e..0bad48e26 100644 --- a/tests/exchange/test_krakenfutures.py +++ b/tests/exchange/test_krakenfutures.py @@ -23,11 +23,11 @@ def test_krakenfutures_get_ft_has(): def test_krakenfutures_ohlcv_candle_limit_caps_at_2000(mocker, default_conf): """Test that OHLCV candle limit is capped at 2000.""" - mocker.patch.object(Exchange, "ohlcv_candle_limit", return_value=5000) - ex = get_patched_exchange(mocker, default_conf, exchange="krakenfutures") assert isinstance(ex, KrakenFutures) + mocker.patch.object(ex, "features", return_value=5000) + assert ex.ohlcv_candle_limit("1m", candle_type=CandleType.FUTURES) == 2000 diff --git a/tests/exchange_online/conftest.py b/tests/exchange_online/conftest.py index efc013297..5d9b0ca67 100644 --- a/tests/exchange_online/conftest.py +++ b/tests/exchange_online/conftest.py @@ -548,6 +548,7 @@ EXCHANGES: dict[str, TestExchangeOnlineSetup] = { "hasQuoteVolume": False, "timeframe": "30m", "futures": True, + "futures_only": True, "candle_count": 5000, "orderbook_max_entries": 20, "futures_pair": "BTC/USDC:USDC", @@ -559,6 +560,19 @@ EXCHANGES: dict[str, TestExchangeOnlineSetup] = { # TODO: re-enable hyperliquid websocket tests "skip_ws_tests": True, }, + "krakenfutures": { + "pair": "BTC/USD:USD", + "stake_currency": "USD", + "hasQuoteVolume": False, + "timeframe": "1h", + "futures": True, + "futures_only": True, + "candle_count": 2000, + "futures_pair": "BTC/USD:USD", + "hasQuoteVolumeFutures": False, + "leverage_tiers_public": False, + "leverage_in_spot_market": False, + }, } EXCHANGES_FUTURES = [exch for exch, params in EXCHANGES.items() if params.get("futures")] @@ -591,11 +605,15 @@ def set_test_proxy(config: Config, use_proxy: bool) -> Config: return config -def get_exchange(exchange_name, exchange_conf): +def get_exchange(exchange_name, exchange_conf, class_mocker=None): exchange_params = EXCHANGES[exchange_name] + if exchange_params.get("futures_only"): + pytest.skip(f"Exchange {exchange_name} is futures-only, skipping spot tests.") exchange_conf = set_test_proxy(exchange_conf, exchange_params.get("use_ci_proxy", False)) exchange_conf["exchange"]["name"] = exchange_name exchange_conf["stake_currency"] = exchange_params["stake_currency"] + if class_mocker: + class_mocker.patch(f"{EXMS}.ft_additional_exchange_init") exchange = ExchangeResolver.load_exchange( exchange_conf, validate=True, load_leverage_tiers=True ) @@ -608,25 +626,30 @@ def get_futures_exchange(exchange_name, exchange_conf, class_mocker): if exchange_params.get("futures") is not True: pytest.skip(f"Exchange {exchange_name} does not support futures.") - else: - exchange_conf = deepcopy(exchange_conf) - exchange_conf = set_test_proxy(exchange_conf, exchange_params.get("use_ci_proxy", False)) - exchange_conf["trading_mode"] = "futures" - exchange_conf["margin_mode"] = "isolated" + exchange_conf = deepcopy(exchange_conf) + exchange_conf = set_test_proxy(exchange_conf, exchange_params.get("use_ci_proxy", False)) + exchange_conf["exchange"]["name"] = exchange_name + exchange_conf["stake_currency"] = exchange_params["stake_currency"] + exchange_conf["trading_mode"] = "futures" + exchange_conf["margin_mode"] = "isolated" - class_mocker.patch("freqtrade.exchange.binance.Binance.fill_leverage_tiers") - class_mocker.patch(f"{EXMS}.fetch_trading_fees") - class_mocker.patch(f"{EXMS}.ft_additional_exchange_init") - class_mocker.patch(f"{EXMS}.load_cached_leverage_tiers", return_value=None) - class_mocker.patch(f"{EXMS}.cache_leverage_tiers") + class_mocker.patch("freqtrade.exchange.binance.Binance.fill_leverage_tiers") + class_mocker.patch(f"{EXMS}.fetch_trading_fees") + class_mocker.patch(f"{EXMS}.ft_additional_exchange_init") + class_mocker.patch(f"{EXMS}.load_cached_leverage_tiers", return_value=None) + class_mocker.patch(f"{EXMS}.cache_leverage_tiers") - return get_exchange(exchange_name, exchange_conf) + exchange = ExchangeResolver.load_exchange( + exchange_conf, validate=True, load_leverage_tiers=True + ) + return exchange, exchange_name @pytest.fixture(params=EXCHANGES, scope="class") def exchange(request, exchange_conf, class_mocker): - class_mocker.patch(f"{EXMS}.ft_additional_exchange_init") - exchange, name, exchange_params = get_exchange(request.param, exchange_conf) + exchange, name, exchange_params = get_exchange( + request.param, exchange_conf, class_mocker + ) yield exchange, name, exchange_params exchange.close() @@ -653,7 +676,7 @@ def exchange_ws(request, exchange_conf, exchange_mode, class_mocker): if exchange_param.get("skip_ws_tests"): pytest.skip(f"{request.param} does not support websocket tests.") if exchange_mode == "spot": - exchange, name, _ = get_exchange(request.param, exchange_conf) + exchange, name, _ = get_exchange(request.param, exchange_conf, class_mocker) pair = exchange_param["pair"] elif exchange_param.get("futures"): exchange, name, _ = get_futures_exchange( diff --git a/tests/exchange_online/test_ccxt_compat.py b/tests/exchange_online/test_ccxt_compat.py index f33818d3d..a5e1e1679 100644 --- a/tests/exchange_online/test_ccxt_compat.py +++ b/tests/exchange_online/test_ccxt_compat.py @@ -64,7 +64,12 @@ class TestCCXTExchange: if not expected_count: pytest.skip("No expected candle count for exchange") - assert exch.ohlcv_candle_limit("1m", CandleType.FUTURES) == expected_count + candle_type = ( + CandleType.FUTURES + if exchange_params.get("futures_only") + else CandleType.SPOT + ) + assert exch.ohlcv_candle_limit("1m", candle_type) == expected_count def test_load_markets_futures(self, exchange_futures: EXCHANGE_FIXTURE_TYPE): exchange, _, exchange_params = exchange_futures