from datetime import timedelta from unittest.mock import MagicMock, PropertyMock import pytest from freqtrade.enums import CandleType, MarginMode, TradingMode from freqtrade.exceptions import OperationalException, RetryableOrderError from freqtrade.exchange.common import API_RETRY_COUNT from freqtrade.util import dt_now, dt_ts from tests.conftest import EXMS, get_patched_exchange from tests.exchange.test_exchange import ccxt_exceptionhandlers @pytest.mark.usefixtures("init_persistence") def test_fetch_stoploss_order_bitget(default_conf, mocker): default_conf["dry_run"] = False mocker.patch("freqtrade.exchange.common.time.sleep") api_mock = MagicMock() exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="bitget") api_mock.fetch_open_orders = MagicMock(return_value=[]) api_mock.fetch_canceled_and_closed_orders = MagicMock(return_value=[]) with pytest.raises(RetryableOrderError): exchange.fetch_stoploss_order("1234", "ETH/BTC") assert api_mock.fetch_open_orders.call_count == API_RETRY_COUNT + 1 assert api_mock.fetch_canceled_and_closed_orders.call_count == API_RETRY_COUNT + 1 api_mock.fetch_open_orders.reset_mock() api_mock.fetch_canceled_and_closed_orders.reset_mock() api_mock.fetch_canceled_and_closed_orders = MagicMock( return_value=[{"id": "1234", "status": "closed", "clientOrderId": "123455"}] ) api_mock.fetch_open_orders = MagicMock(return_value=[{"id": "50110", "clientOrderId": "1234"}]) resp = exchange.fetch_stoploss_order("1234", "ETH/BTC") assert api_mock.fetch_open_orders.call_count == 2 assert api_mock.fetch_canceled_and_closed_orders.call_count == 2 assert resp["id"] == "1234" assert resp["id_stop"] == "50110" assert resp["type"] == "stoploss" default_conf["dry_run"] = True exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="bitget") dro_mock = mocker.patch(f"{EXMS}.fetch_dry_run_order", MagicMock(return_value={"id": "123455"})) api_mock.fetch_open_orders.reset_mock() api_mock.fetch_canceled_and_closed_orders.reset_mock() resp = exchange.fetch_stoploss_order("1234", "ETH/BTC") assert api_mock.fetch_open_orders.call_count == 0 assert api_mock.fetch_canceled_and_closed_orders.call_count == 0 assert dro_mock.call_count == 1 def test_fetch_stoploss_order_bitget_exceptions(default_conf_usdt, mocker): default_conf_usdt["dry_run"] = False api_mock = MagicMock() # Test emulation of the stoploss getters api_mock.fetch_canceled_and_closed_orders = MagicMock(return_value=[]) ccxt_exceptionhandlers( mocker, default_conf_usdt, api_mock, "bitget", "fetch_stoploss_order", "fetch_open_orders", retries=API_RETRY_COUNT + 1, order_id="12345", pair="ETH/USDT", ) def test_bitget_ohlcv_candle_limit(mocker, default_conf_usdt): # This test is also a live test - so we're sure our limits are correct. api_mock = MagicMock() api_mock.options = { "fetchOHLCV": { "maxRecentDaysPerTimeframe": { "1m": 30, "5m": 30, "15m": 30, "30m": 30, "1h": 60, "4h": 60, "1d": 60, } } } exch = get_patched_exchange(mocker, default_conf_usdt, api_mock, exchange="bitget") timeframes = ("1m", "5m", "1h") for timeframe in timeframes: assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT) == 1000 assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES) == 1000 assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK) == 1000 assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE) == 200 start_time = dt_ts(dt_now() - timedelta(days=17)) assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT, start_time) == 1000 assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == 1000 assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == 1000 assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 200 start_time = dt_ts(dt_now() - timedelta(days=48)) length = 200 if timeframe in ("1m", "5m") else 1000 assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT, start_time) == length assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == length assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == length assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 200 start_time = dt_ts(dt_now() - timedelta(days=61)) length = 200 assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT, start_time) == length assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == length assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == length assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 200 def test_additional_exchange_init_bitget(default_conf, mocker): default_conf["dry_run"] = False default_conf["trading_mode"] = TradingMode.FUTURES default_conf["margin_mode"] = MarginMode.ISOLATED api_mock = MagicMock() api_mock.set_position_mode = MagicMock(return_value={}) get_patched_exchange(mocker, default_conf, exchange="bitget", api_mock=api_mock) assert api_mock.set_position_mode.call_count == 1 ccxt_exceptionhandlers( mocker, default_conf, api_mock, "bitget", "additional_exchange_init", "set_position_mode" ) def test_dry_run_liquidation_price_cross_bitget(default_conf, mocker): default_conf["dry_run"] = True default_conf["trading_mode"] = TradingMode.FUTURES default_conf["margin_mode"] = MarginMode.CROSS api_mock = MagicMock() mocker.patch(f"{EXMS}.get_maintenance_ratio_and_amt", MagicMock(return_value=(0.005, 0.0))) exchange = get_patched_exchange(mocker, default_conf, exchange="bitget", api_mock=api_mock) with pytest.raises( OperationalException, match="Freqtrade currently only supports isolated futures for bitget" ): exchange.dry_run_liquidation_price( "ETH/USDT:USDT", 100_000, False, 0.1, 100, 10, 100, [], ) def test__lev_prep_bitget(default_conf, mocker): api_mock = MagicMock() api_mock.set_margin_mode = MagicMock() api_mock.set_leverage = MagicMock() type(api_mock).has = PropertyMock(return_value={"setMarginMode": True, "setLeverage": True}) exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="bitget") exchange._lev_prep("BTC/USDC:USDC", 3.2, "buy") assert api_mock.set_margin_mode.call_count == 0 assert api_mock.set_leverage.call_count == 0 # test in futures mode api_mock.set_margin_mode.reset_mock() api_mock.set_leverage.reset_mock() default_conf["dry_run"] = False default_conf["trading_mode"] = "futures" default_conf["margin_mode"] = "isolated" exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="bitget") exchange._lev_prep("BTC/USDC:USDC", 3.2, "buy") assert api_mock.set_margin_mode.call_count == 0 assert api_mock.set_leverage.call_count == 1 api_mock.set_leverage.assert_called_with(symbol="BTC/USDC:USDC", leverage=3.2) api_mock.reset_mock() exchange._lev_prep("BTC/USDC:USDC", 19.99, "sell") assert api_mock.set_margin_mode.call_count == 0 assert api_mock.set_leverage.call_count == 1 api_mock.set_leverage.assert_called_with(symbol="BTC/USDC:USDC", leverage=19.99)